Spring –持久层–编写实体并配置Hibernate
在开始之前,我们需要更新我们的Maven依赖项,因为我们现在将使用Hibernate和Spring。 将以下依赖项添加到pom.xml中 :
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.6.8.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!-- spring framework --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.1.0.RELEASE</version> </dependency>
如果您是Maven的新手,您可能现在想知道-您怎么知道这些? 我在哪里可以买到? 好吧,只需转到http://mvnrepository.com/并输入您要搜索的啤酒。 您将获得有关Maven依赖项的完整代码。 如果您曾经尝试在不使用Maven的情况下自己组装Spring或Hibernate应用程序,那么您可能知道它是多么痛苦。 使用Maven,事情变得简单得多。
还要注意,我们包括了对MySQL连接器的依赖。 如果您决定使用其他数据库,请不要忘记更改它。
使用Hibernate,我们有2种选择如何将POJO变成实体。 我们要么使用XML并创建映射文件 ,要么将一些元信息放入我们的代码(java批注)中。 有些人对此感到恐惧,并认为这是与框架的结合。 的确,您在类路径中将需要javax.persistence批注,但我们不会实现接口或扩展框架类。 我们将只在代码中添加一些元信息,而POJO仍然只是带有一些额外信息的POJO。
现在,我们将POJO转换为实体。 我们将需要进行以下更改:
- 为Hibernate添加默认的无参数构造函数
- 为字段创建获取器和设置器
- 添加equals和hashCode方法。
- 添加持久性注释。 请注意,我们还使用@Table批注来区分Java和SQL命名约定。
- 添加ID字段。 这些将是我们关系数据库中的主键。
这是很多样板代码,因此让您的IDE帮您。 大多数现代IDE都会为您生成构造函数,getter,setter,equals和hashCode。
package org.timesheet.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = 'employee') public class Employee { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String name; private String department; public Employee() { } public Employee(String name, String department) { this.name = name; this.department = department; } public String getName() { return name; } public String getDepartment() { return department; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setName(String name) { this.name = name; } public void setDepartment(String department) { this.department = department; } @Override public String toString() { return 'Employee [id=' + id + ', name=' + name + ', department=' + department + ']'; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((department == null) ? 0 : department.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Employee)) { return false; } Employee other = (Employee) obj; if (department == null) { if (other.department != null) { return false; } } else if (!department.equals(other.department)) { return false; } if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } }
package org.timesheet.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = 'manager') public class Manager { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String name; public Manager() { } public Manager(String name) { this.name = name; } public String getName() { return name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Manager)) { return false; } Manager other = (Manager) obj; if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } @Override public String toString() { return 'Manager [id=' + id + ', name=' + name + ']'; } }
package org.timesheet.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; @Entity @Table(name='timesheet') public class Timesheet { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @OneToOne @JoinColumn(name = 'employee_id') private Employee who; @OneToOne @JoinColumn(name = 'task_id') private Task task; private Integer hours; public Timesheet() { } public Timesheet(Employee who, Task task, Integer hours) { this.who = who; this.task = task; this.hours = hours; } public Employee getWho() { return who; } public Task getTask() { return task; } public Integer getHours() { return hours; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setWho(Employee who) { this.who = who; } public void setTask(Task task) { this.task = task; } public void setHours(Integer hours) { this.hours = hours; } /** * Manager can alter hours before closing task * @param hours New amount of hours */ public void alterHours(Integer hours) { this.hours = hours; } @Override public String toString() { return 'Timesheet [id=' + id + ', who=' + who + ', task=' + task + ', hours=' + hours + ']'; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((hours == null) ? 0 : hours.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((task == null) ? 0 : task.hashCode()); result = prime * result + ((who == null) ? 0 : who.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Timesheet)) { return false; } Timesheet other = (Timesheet) obj; if (hours == null) { if (other.hours != null) { return false; } } else if (!hours.equals(other.hours)) { return false; } if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (task == null) { if (other.task != null) { return false; } } else if (!task.equals(other.task)) { return false; } if (who == null) { if (other.who != null) { return false; } } else if (!who.equals(other.who)) { return false; } return true; } }
最后,这是我们需要同时使用@ManyToMany映射的Task实体。 这是因为一名雇员可以从事多个任务,而一项任务可以分配多个雇员。 我们使用@JoinTable和@JoinColumn批注定义了m:n的外观。
package org.timesheet.domain; import javax.persistence.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @Entity @Table(name = 'task') public class Task { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = 'task_employee', joinColumns = {@JoinColumn(name = 'task_id')}, inverseJoinColumns = {@JoinColumn(name = 'employee_id')} ) private List<Employee> assignedEmployees = new ArrayList<Employee>(); @OneToOne @JoinColumn(name = 'manager_id') private Manager manager; private String description; boolean completed; public Task() { } public Task(String description, Manager manager, Employee... employees) { this.description = description; this.manager = manager; assignedEmployees.addAll(Arrays.asList(employees)); completed = false; } public Manager getManager() { return manager; } public List<Employee> getAssignedEmployees() { return assignedEmployees; } public void addEmployee(Employee e) { assignedEmployees.add(e); } public void removeEmployee(Employee e) { assignedEmployees.remove(e); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public boolean isCompleted() { return completed; } public void setCompleted(boolean completed) { this.completed = completed; } public void setAssignedEmployees(List<Employee> assignedEmployees) { this.assignedEmployees = assignedEmployees; } public void setManager(Manager manager) { this.manager = manager; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Task task = (Task) o; if (completed != task.completed) { return false; } if (description != null ? !description.equals(task.description) : task.description != null) { return false; } if (id != null ? !id.equals(task.id) : task.id != null) { return false; } if (manager != null ? !manager.equals(task.manager) : task.manager != null) { return false; } return true; } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (manager != null ? manager.hashCode() : 0); result = 31 * result + (description != null ? description.hashCode() : 0); result = 31 * result + (completed ? 1 : 0); return result; } @Override public String toString() { return 'Task{' + 'id=' + id + ', assignedEmployees=' + assignedEmployees + ', manager=' + manager + ', description='' + description + '\'' + ', completed=' + completed + '}'; } }
因此,我们实际上并没有做任何特别的建模。 如果您喜欢一些UML,请看下图,关系与以前相同。
好的,我们有实体,现在让我们创建数据库。 选择一些数据库管理工具(即使是普通终端也可以)并创建时间表数据库(默认情况下,mysql将在Mac OS X上安装到/ usr / local / mysql / bin / mysql):
$ mysql -u root mysql > create database timesheet;
如果您可能在不了解之前就已经配置过Hibernate,那么在处理SessionFactory时就需要很多文件和样板代码。 使用Spring,这些事情要简单得多。
现在,我们将创建我们的第一个Spring Bean配置文件 -该文件在其中注册Spring容器的bean。 如果我不得不向根本不了解Spring的人解释这个文件是什么,那是Spring容器可以找到对象的神奇包。
现代IDE将帮助您正确使用所有XML名称空间,例如,您可以从STS向导中查看图片。 NetBeans具有类似的功能,IntelliJ可以动态解析名称空间。
命名配置文件persistence-beans.xml,然后将其放在src / main / resources文件夹下。
因此,设置Hibernate,事务,批注配置等就像在XML文件中创建几个bean一样简单。 另外,我们可以将Java Config用于Spring,但是XML config仍然使用更多,因此我们将坚持使用这些配置。 我不想阻止您使用Java Config! XML config 现在更受欢迎,但是我不能保证在接下来的几年中。
我已经评论了每个bean,以确保您在继续之前了解我们在这里所做的事情。 如果要直观地了解Bean之间的连接,则可以再次使用一些工具-在STS中将其称为Bean Graph,在IntelliJ中将其称为Dependencies。 您可以在下面的图片中看到依赖项示例。
<?xml version='1.0' encoding='UTF-8'?> <beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:context='http://www.springframework.org/schema/context' xmlns:tx='http://www.springframework.org/schema/tx' xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd'> <!-- we can use annotations --> <context:annotation-config /> <!-- package to look for annotated classes --> <context:component-scan base-package='org.timesheet.service.impl' /> <!-- we will manage transactions with annotations --> <tx:annotation-driven /> <!-- data source for our database --> <bean id='dataSource' class='org.springframework.jdbc.datasource.DriverManagerDataSource'> <property name='driverClassName' value='com.mysql.jdbc.jdbc2.optional.MysqlDataSource' /> <property name='url' value='jdbc:mysql://localhost/timesheet' /> <property name='username' value='root' /> <property name='password' value='' /> </bean> <!-- configure hibernate session factory --> <bean id='sessionFactory' class='org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean'> <property name='dataSource' ref='dataSource' /> <property name='annotatedClasses' > <list> <value>org.timesheet.domain.Employee</value> <value>org.timesheet.domain.Manager</value> <value>org.timesheet.domain.Task</value> <value>org.timesheet.domain.Timesheet</value> </list> </property> <property name='hibernateProperties'> <props> <prop key='dialect'>org.hibernate.dialect.MySQL5InnoDBDialect</prop> <prop key='hibernate.show_sql'>true</prop> <prop key='hibernate.hbm2ddl.auto'>update</prop> </props> </property> </bean> </beans>
好的,那是很多配置,对吗? 不好的是,我们已经将实体的名称作为纯文本放置到XML中,因此它不是友好的重构形式。 但我认为对于本教程来说是可以接受的 让我们为Hibernate编写集成测试,以便我们知道一切都已正确设置。
package org.timesheet.integration; import static org.junit.Assert.*; import org.hibernate.SessionFactory; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; @ContextConfiguration(locations = '/persistence-beans.xml') public class HibernateConfigurationTest extends AbstractJUnit4SpringContextTests { @Autowired private SessionFactory sessionFactory; @Test public void testHibernateConfiguration() { // Spring IOC container instantiated and prepared sessionFactory assertNotNull (sessionFactory); } }
我要你在这里注意两件事。 首先,我们扩展AbstractJUnit4SpringContextTests类。 我们告诉它应该在哪里寻找使用Spring bean的实际XML配置。 否则,我们将不得不自己创建Spring容器,这意味着需要更多样板代码。
其次,我们使用@Autowired批注。 这意味着我们不会使用新的运算符手动创建SessionFactory实例,而是将其通过Spring容器自动装配(注入)! 这是Spring容器最重要的目的之一-依赖于接口并注入实现而不是手工创建它们。
一切现在都应该正常工作,我认为这部分就足够了。
如果您愿意,可以检查普通的SQL,并在此处查看表,请按照以下步骤进行操作:
mysql> use timesheet; mysql> show tables; +---------------------+ | Tables_in_timesheet | +---------------------+ | employee | | manager | | task | | task_employee | | timesheet | +---------------------+ 5 rows in set (0.00 sec)
参考: 第2部分–持久层–从vrtoonjava博客的JCG合作伙伴 Michal Vrtiak 编写实体并配置Hibernate 。
翻译自: https://www.javacodegeeks.com/2012/09/spring-persistence-layer-writing.html