Jpa之关联对象(单向一对多)

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就不会生成中间表!


{3IY_$3PJ$MS3{`UYF487LQ.png

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("参数为多方需要排序的属性名")对指定的属性排序!

你可能感兴趣的:(Jpa之关联对象(单向一对多))