1.对象设计
Employee 实体
@Entity
@Setter
@Getter
public class Employee {
@Id
@GeneratedValue
private Long id;
private String name;//名称
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Department 实体
@Getter
@Setter
@ToString
@Entity
public class Department {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany//建立关联一对多
private List employees = new ArrayList<>();
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
2.表的结构
![FOOL`_MGPO%PQUZL)OV76V.png
从图中可以看出,对于一对多的关联方式,jpa内部会采取生成一张中间表来建立一方和多方之间的关系,因为多了一张表,这种方式会浪费性能。
无论是单向一对多,单向多对一,双向一对多,多对一,表的结构是不变的!应该和多对一的结构是一样的!
@Getter
@Setter
@ToString
@Entity
public class Department {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany//建立关联一对多
@JoinColumn(name = "dept_id")//指定关联列的名字
private List employees = new ArrayList<>();
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
此时使用@JoinColumn(name = "dept_id")指定外键,jpa就不会生成中间表!
3.实现
向表中添加数据:
(1):先保存部门,再保存员工
@Test
public void testsave(){
EntityManager entityManager = JPAUtil.createEntityManager();
//创建一方
Department department = new Department();
department.setName("行政部");
//创建多方
Employee e1 = new Employee();
e1.setName("lucy");
Employee e2 = new Employee();
e2.setName("cici");
department.getEmployees().add(e1);
department.getEmployees().add(e2);
//持久化
entityManager.persist(department);
entityManager.persist(e1);
entityManager.persist(e2);
entityManager.getTransaction().begin();
entityManager.getTransaction().commit();
entityManager.close();
}
(2):先保存员工,再保存部门
@Test
public void testsave(){
EntityManager entityManager = JPAUtil.createEntityManager();
//创建一方
Department department = new Department();
department.setName("行政部");
//创建多方
Employee e1 = new Employee();
e1.setName("lucy");
Employee e2 = new Employee();
e2.setName("cici");
department.getEmployees().add(e1);
department.getEmployees().add(e2);
//持久化
entityManager.persist(e1);
entityManager.persist(e2);
entityManager.persist(department);
entityManager.getTransaction().begin();
entityManager.getTransaction().commit();
entityManager.close();
}
4.添加SQL分析
(1):先保存部门,再保存员工
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into Employee (name) values (?)
Hibernate: update Employee set dept_id=? where id=?
Hibernate: update Employee set dept_id=? where id=?
执行了5条SQL:
(2):先保存员工,再保存部门
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into Employee (name) values (?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set dept_id=? where id=?
Hibernate: update Employee set dept_id=? where id=?
发现不管是先保存一方还是多方都会发送5条SQL,因为外键id是由一方来管理,所以它必须执行相应的update更新操作!
5.查询的SQL分析
@Test
public void testFind(){
EntityManager entityManager = JPAUtil.createEntityManager();
//获取一方
Department department = entityManager.find(Department.class, 1l);
entityManager.close();
}
发送的SQL
Hibernate : SELECT
employee0_.id AS id1_1_0_,
employee0_. NAME AS name2_1_0_
FROM
Employee employee0_
WHERE
employee0_.id =?
并没有去执行查询多方的信息,说明默认是延迟加载!
@Test
public void testFind(){
EntityManager entityManager = JPAUtil.createEntityManager();
//获取一方
Department department = entityManager.find(Department.class, 1l);
entityManager.close();
System.out.println(department.getEmployees());
}
抛出异常:延迟初始化异常,也说明了默认是延迟加载
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: hanfengyi.one2many.Department.employees, could not initialize proxy - no Session
6.判断集合中的数据
通过一方获取多方,判断集合是否有数据!不能够直接判断是否为null,因为在实体中为防止报错,已经初始化了。所以无论如何它都不为null。
@Test
public void testFind(){
EntityManager entityManager = JPAUtil.createEntityManager();
//获取一方
Department department = entityManager.find(Department.class, 1l);
if(department.getEmployees().size()!=0){
System.out.println("有数据");
}
entityManager.close();
}
7.集合映射
List集合
@Test
public void testLMapping(){
EntityManager entityManager = JPAUtil.createEntityManager();
Department department = entityManager.find(Department.class, 1l);
System.out.println(department.getEmployees().getClass());//class org.hibernate.collection.internal.PersistentBag
entityManager.close();
}
Set集合
@Test
public void testLMapping(){
EntityManager entityManager = JPAUtil.createEntityManager();
Department department = entityManager.find(Department.class, 1l);
System.out.println(department.getEmployees().getClass());//class org.hibernate.collection.internal.PersistentSet
entityManager.close();
}
这里是Hibernate为了实现延迟加载而提供的类
org.hibernate.collection.internal.PersistentSet 实现了java.util.Set接口
org.hibernate.collection.internal.PersistentBag 实现了java.util.List接口
我们声明集合的时候必须使用接口,因为Hibernate是用它自己的List/Set实现类PersistentBag /PersistentSet 来接收数据!如果我们直接使用实现类初始化,PersistentBag /PersistentSet 和ArrayList/HashSet是平级的,会抛出异常。
8.List映射
Set:无序不重复
List:有序可重复
使用List集合排序的方式:
在集合的上方使用注解 @OrderBy("参数为多方需要排序的属性名")
@Getter
@Setter
@ToString
@Entity
public class Department {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany//建立关联一对多
@JoinColumn(name = "dept_id")//指定关联列的名字
@OrderBy("name")//将name属性进行排序,默认是升序
private List employees = new ArrayList<>();
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Test
public void testFind(){
EntityManager entityManager = JPAUtil.createEntityManager();
//获取一方
Department department = entityManager.find(Department.class, 1l);
if(department.getEmployees().size()!=0){
System.out.println("有数据");
System.out.println(department.getEmployees());
//结果:[Employee{id=2, name='cici'}, Employee{id=1, name='lucy'}]
}
entityManager.close();
}
小结:
1.一对多与多对一的表的结构是一样的,只是站的角度不同。
2.一对多的关联方式使用注解@OneToMany,jpa会采取生成中间表来关联。
3.在一方使用@JoinColumn(name = " ")指定关联列的名字,就不会生成中间表。
4.无论是先保存一方还是先保存多方,执行的SQL语句条数是一样的!因为外键是由一方来处理的,所以一对多的性能很差,推荐使用双向!
5.一对多默认是延迟加载,因为多方是集合,延迟加载可以很好的节约性能!
6.集合必须使用接口声明,因为Hibernate要用到他自己的实现类来接收数据!
7.判断集合中的数据不能直接判断为null,因该判断它的size是否不等于0!
8.List集合可以使用注解@OrderBy("参数为多方需要排序的属性名")对指定的属性排序!