JPA快速入门(二)

本文深入探讨JPA中的关联关系映射,包括单向多对一、单向一对多、双向多对一的表结构设计、对象关系、SQL分析及实现。特别强调了延迟加载的使用,分析了在不同保存顺序下对数据库操作的影响,展示了如何通过fetch属性控制加载策略。此外,还涉及集合映射、组件关系和继承关系的映射方法。
摘要由CSDN通过智能技术生成

大纲:

  1. 对象关系映射

    1. 关联关系映射
      1. 单向多对一
      2. 单向一对多
      3. 双向多对一
      4. 双向多对多
    2. 集合映射
    3. 组件关系映射
    4. 继承关系映射
  2. 数据查询

    1. Query查询
    2. NamedQuery查询
    3. NativeQuery查询
  3. 事务并发访问

    1. 隔离级别
    2. 悲观锁
    3. 乐观锁
  4. 一级缓存

  5. 二级缓存


对象关系

  1. 依赖关系
    如果A对象离开了B对象,A对象就不能正常编译,则A对象依赖B对象.
    在A类使用到了B(调用了B的方法,调用了B的字段).
  2. 关联关系
    A对象依赖B对象,并且把B对象作为A对象的一个字段,则A和B是关联关系.

按照多重性分:
1).一对一:一个A对象属于一个B对象,一个B对象属于一个A对象.
比如:QQ号码对应一个QQ空间.
2).一对多:一个A对象包含多个B对象.
比如:一个部门包含多个员工对象,此时我们使用集合来封装B对象..
3).多对一:多个A对象同属于一个B对象,并且每个A对象只能属于一个B对象.
比如:多个员工对象属于同一个部门.
设计表的时候:外键在many这一方/在开发设计中:添加一个many方对象的时候,往往通过下拉列表去选择one方.
4).多对多:一个A对象属于多个B对象,一个B对象属于多个A对象.
比如:一个老师可以有多个学生,一个学生可以有多个老师.
通过中间表来表示关系.
按照导航性分:如果通过A对象中的某一个属性可以访问该属性对应的B对象,则说A可以导航到B.
1).单向:只能从A通过属性导航到B,B不能导航到A.
2).双向:A可以通过属性导航到B,B也可以通过属性导航到A.
判断方法:
1,判断都是从对象的实例上面来看的;
2,判断关系必须确定一对属性;
3,判断关系必须确定具体需求;

  1. 聚合关系
    表示整体和部分的关系,整体和部分之间可以相互独立存在,一定是有两个模块来分别管理整体和部分.整体和部分可以分开.

  2. 组合关系
    强聚合关系,但是整体和部分不能独立存在,一定是在一个模块中同时管理整体和部分,生命周期必须相同.如:单据和单据明细/购物车信息

  3. 泛化关系
    其实就是继承关系.

在实际开发中,我们需要去维护对象之间的关系,意思是说,在保存A的时候,需要考虑到关联的B,在查询到A之后,如何将关联的B对象查询到

常用关联关系映射

  • 单向多对一
    在实际开发中,绝大部分的关联关系都是单向多对一,所以我们需要重点掌握

以员工和部门的关系为例

  1. 表结构的设计
    通常,多对一的关系,可以在多方的表中添加一个外键列来维护双方的关系,如:
    image.png

  2. 对象的设计

    @Getter@Setter
    public class Employee {
     private Long id;
     private String name;
     //封装当前员工所在的部门信息
     private Department dept;
    }
    
    @Getter@Setter
    public class Department {
     private Long id;
     private String name;
    }
    

    可以看到,在表结构方面,我们应该是在employee表中添加外键列来维护员工和部门的关系
    在对象设计方面,在Employee对象中关联Department对象,来封装当前员工所在的部门信息

  3. SQL分析
    保存(两个员工一个部门)
    因为是在多方(employee)关联one方(department)的数据,所以,在保存employee的时候需要考虑关系数据的维护(外键列的信息)

先保存部门,再保存员工

保存部门信息,将数据库自动生成的主键信息作为保存员工时的dept_id的信息
INSERT INTO department(name) VALUES(?);
这里的dept_id是刚刚保存的部门信息的id
INSERT INTO employee(name,dept_id) VALUES(?,?);
INSERT INTO employee(name,dept_id) VALUES(?,?);

先保存员工,再保存部门

此时dept_id为null
Employee emp = new Employee();
emp.setName(“Neld”);
emp.setDept(null);
INSERT INTO employee(name,dept_id) VALUES(?,null);
INSERT INTO employee(name,dept_id) VALUES(?,null);

INSERT INTO department(name) VALUES(?,?);

这三条sql执行之后,员工表的外键列为null,此时,需要发送两条update语句将dept_id更新进去
UPDATE employee SET dept_id = ?,name=? WHERE id = ?
UPDATE employee SET dept_id = ?,name=? WHERE id = ?

此时需要多发送两条sql才能将数据保存完整,所以,这种情况,建议选择先保存one方,再保存many方,尽量减少sql的发送

查询

查询id为1的员工,并获取到当前员工所在的部门信息
SELECT id,name,dept_id FROM employee WHERE id = ?;
查询部门是根据当前员工所在的部门的编号查询
SELECT id,name FROM department WHERE id = ?;

实现

使用JPA实现many2one的映射关系非常的简单,只需要在关联的对象上贴上@Many2One的注解即可,如:

@Getter
@Setter
@Entity
public class Employee {

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToOne
    private Department dept;
}

测试代码

public void testSave() throws Exception {

    Employee e1 = new Employee();
    e1.setName("Neld");

    Employee e2 = new Employee();
    e2.setName("Lucy");

    Department dept = new Department();
    dept.setName("研发部");

    //设置关联关系
    e1.setDept(dept);
    e2.setDept(dept);

    EntityManager em = JPAUtil.getEntityManager();
    em.getTransaction().begin();
    //先保存one方,再保存many方
    em.persist(dept);
    em.persist(e1);
    em.persist(e2);

    em.getTransaction().commit();
    em.close();
}

执行的SQL:

Hibernate: insert into Department (name) values (?)

Hibernate: insert into Employee (dept_id, name) values (?, ?)

Hibernate: insert into Employee (dept_id, name) values (?, ?)

和预期的sql一致

如果先保存many方,再保存one方呢? 如:

em.persist(e1);
em.persist(e2);
em.persist(dept);

执行的SQL:

Hibernate: insert into Employee (dept_id, name) values (?, ?)
Hibernate: insert into Employee (dept_id, name) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set dept_id=?, name=? where id=?
Hibernate: update Employee set dept_id=?, name=? where id=?

可以看出,多执行了两条update语句来更新外键列的信息,而且更新语句中更新了除dept_id外键列以外的其他列的信息,说明这两条update语句必定是many方发出的,因为在one方是不知道有这些信息的

分析update语句的执行原理:

  1. 执行insert语句将Employee保存到数据库中,此时Employee对象由瞬时状态转换成持久状态,此时Employee所依赖的Department的id为null
  2. 保存Employee对象所依赖的Department对象,获取到数据库中自动生成的主键,此时一级缓存中的Employee对象发生改变(依赖的Department对象的id属性发生改变)
  3. 提交事务,检查缓存中的Employee对象和快照区域的Employee对象是否一致,发现两者所依赖的Department对象的主键信息是不一致的
  4. 一级缓存和快照区数据不一致,在提交事务的时候会执行对应的update语句将缓存中的脏数据持久化到数据库中

数据保存的细节分析:
现在创建表结构的sql是自动生成的,我们来做一个简单的分析

Hibernate: create table Employee (id bigint not null auto_increment, name varchar(255), dept_id bigint, primary key (id))

从建表语句中可以看出,为我们自动生成了关联Department的外键信息(dept_id)
外键列是有属性名_id组成的,如果需要修改,可以使用@JoinColumn注解修改外键列的信息

@ManyToOne
//修改外键列的相关信息(列名/约束/类型等)
@JoinColumn(name = "department_id")
private Department dept;

查询分析

测试代码

public void testGet() throws  Exception{

    EntityManager em = JPAUtil.getEntityManager();
    Employee e = em.find(Employee.class, 1L);

    System.out.println(e.getName());

    System.out.println(e.getDept().getName());

    em.close();
}

执行结果:

Neld

研发部

查询到了many方和many方关联的one的数据

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值