持久化类
Hibernate是持久层的ORM映射框架,专注于数据的持久化工作。
所谓的持久化,就是将内存中的数据永久存储到关系型数据库中。
所谓的持久化类指的是一个Java类与数据库表建立了映射关系,那么这个类称为是持久化类。
其实你可以简单的理解为持久化类就是一个Java类有了一个映射文件与数据库的表建立了关系。
那么我们编写持久化类的时候有哪些要求呢?
实体类编写规则
我们在编写持久化类的时候需要有一下几点需要注意:
持久化类需要提供无参数的构造方法。因为在 Hibernate 的底层需要使用反射生成类的实例。
持久化类的属性需要私有,对私有的属性提供公有的
get
和set
方法。因为在
Hibernate
底层会将查询到的数据进行封装。也就是说,实体类里面属性是私有的持久化类的属性要尽量使用包装的类型。
因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。举个例子:
1
假设表中有一列员工工资,如果使用double类型,如果这个员工工资忘记录入到系统中,系统会将默认值0存入到数据库,如果这个员工的工资被扣完了,也会向系统中存入0.那么这个0就有了多重含义,而如果使用包装类类型就会避免以上情况。如果使用Double类型,忘记录入的工资就会存入null,而如果这个员工的工资被扣完了,就会存入0,不会产生歧义。
持久化类要有一个唯一标识OID与表的主键对应,也就是要求实体类有属性作为唯一值(一般使用id值)
因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象,在关系型数据库的表中是通过主键区分是否是同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个OID相同的持久化对象的。
持久化类尽量不要使用final进行修饰。
因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么就不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略(是一种优化手段)就会失效。
持久化类我们已经可以正常编写了,但是在持久化类中需要有一个唯一标识OID与表的主键去建立映射关系。而且主键一般我们是不会让客户手动录入的,一般我们是由程序生成主键。那么Hibernate中也提供了相应的主键生成的方式,下面我们来看Hibernate的主键生成策略。
主键生成策略
主键的类型
在讲解Hibernate的主键生成策略之前,先来了解两个概念,即自然主键和代理主键,具体如下:
自然主键(少见):把具体业务含义的字段作为主键,称之为自然主键。
例如在customer表中,如果把name字段作为主键,其前提条件必须是:每一个客户的名字不允许为null,不允许客户重名,并且不允许修改客户姓名。尽管这也是可行的,但是不能满足不断变化的业务需求,一旦出现了允许客户重名的业务需求,就必须修改数据模型,重新定义表的主键,这给数据库的维护增加了难度。
代理主键:把不具备业务含义的字段作为主键(一般是ID),称之为代理主键。
该字段一般取名为”ID“,通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间。在上面的例子中,显然更合理的方式是使用代理主键。
主键生成策略
hibernate要求实体类里面有一个属性作为唯一值,对应表主键,主键可以不同生成策略
Hibernate中,提供了几个内置的主键生成策略,其常用主键生成策略的名称和描述如下:
- 自然主键
- assigned:自然主键生成策略. hibernate不会管理主键值.由开发人员自己录入。如果不知道 id 元素的generator属性,则默认使用该主键生成策略。
- 代理主键
- identity:主键自增.要求在数据库中把主键定义为自增长类型.录入时不需要指定主键.
- sequence:Oracle中的主键生成策略.
- increment(了解):主键自增.由hibernate来维护.每次插入前会先查询表中id最大值.+1作为新主键值.只有当没有其他进程向同一张表中插入数据时才可以使用,不能在集群环境下使用。
- native:hilo+sequence+identity 自动三选一策略.适合跨数据库平台开发 自动增长
- uuid:产生随机字符串作为主键. 主键类型必须为string 类型.

持久化对象的状态
持久化对象的三种状态
Hibernate为了更好的来管理持久化类,将持久化类分成了三种状态:瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一中。
1、瞬时态(transient)
对象里面没有id值,对象与session没有关联,不处于 Session 的缓存中
瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hibernate的Session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是孤立存在的,与数据库的数据没有任何关联,仅是一个信息携带的载体。
2、持久态(persistent)
对象里面有id值,对象与session关联,也是持久化对象
持久态的对象存在持久化标识OID,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。
3、脱管态(detached)
对象有id值,对象与session没有关联
脱管态也称为离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。
区分对象的三种状态
1 |
|
customer对象由new关键字创建,此时还未与Session进行关联,它的状态称为瞬时态;在执行了session.save(customer)操作后,customer对象纳入了Session的管理范围,这时的customer对象变成了持久态对象,此时Session的事务还没有被提交;程序执行完commit()操作并关闭了Session后,customer对象与Session的关联被关闭,此时customer对象就变成了脱管态。
三种状态的转换

从图中可以看出,当一个对象被执行new关键字创建后,该对象处于瞬时态;当对瞬时态对象执行Session的save()或saveOrUpdate()方法后,该对象将被放入Session的一级缓存,对象进入持久态;当对持久态对象执行evict()、close()或clear()操作后,对象进入脱管态(游离态);当直接执行Session的get()、load()、find()或iterate()等方法从数据库里查询对象时,查询到的对象也处于持久态;当对数据库中的记录进行update()、saveOrUpdate()以及lock()等操作后,此时脱管态的对象就过渡到持久态;由于瞬时态和脱管态的对象不在Session的管理范围,所以一段时间后会被JVM回收。
持久化对象的三种状态可以通过调用Session中的一系列方法实现状态间的转换,具体如下:
瞬时态转换到其他状态
通过前面学习可知,瞬时态的对象由new关键字创建,瞬时态对象转换到其他状态总结如下:
- 瞬时态转换为持久态:执行Session的save()或saveOrUpdate()方法。
- 瞬时态转换为脱管态:为瞬时态对象设置持久化标识OID。
由于持久化对象状态演化图中没有涉及到瞬时态转换到脱管态的情况,这里做下简要的说明,在前面的学习中可知,脱管态对象存在OID,但是没有Session的关联,也就是说脱管态和瞬时态的区别就是OID有没有值,所以可以通过为瞬时态对象设置OID,使其变成脱管态对象。
持久态对象转换到其他状态
持久化对象可以直接通过Hibernate中Session的get()、load()方法,或者Query查询从数据库中获得,持久态对象转换到其他状态总结如下:
- 持久态转换为瞬时态:执行Session的
delete()
方法,需要注意的是被删除的持久化对象,不建议再次使用。 - 持久态转换为脱管态:执行Session的
evict()
、close()
或clear()
方法。evict()
方法用于清楚一级缓存中某一对象;close()
方法用于关闭Session,清楚一级缓存;clear()
方法用于清除一级缓存的所有对象。
脱管态对象转换到其他状态
脱管态对象无法直接获得,是由其他状态对象转换而来的,脱管态对象转换到其他状态总结如下:
- 脱管态转换为持久态:执行Session的
update()
、saveOrUpdate()
或lock()
方法。 - 脱管态转化为瞬时态:将脱管态对象的持久化标识OID设置为
null
。
由于持久化对象状态演化图中没有涉及到脱管态转换到瞬时态的情况,这里做下简要的说明,跟瞬时态转换到脱管态的情况相似,脱管态和瞬时态的区别就是OID有没有值,所有可以通过将脱管态对象的OID设置为null,使其变成瞬时态对象。例如在session.close()
操作后,加入代码customer.setCust_id(null)
,customer对象将由脱管态转化为瞬时态。
持久态对象能够自动更新数据库
1 | // 测试持久化类的持久化对象有自动更新数据库的能力 |
执行测试我们会发现,我们并没有手动调用update方法,Hibernate就可以将数据自动更新了。持久态对象之所以有这样的一个功能,其实都依赖了HIbernate的一级缓存。
这一部分参考 https://www.cnblogs.com/yft-javaNotes/p/10244422.html
Hibernate的一级缓存
hibernate的一级缓存默认打开的
hibernate的一级缓存使用范围,是session范围,从session创建到session关闭范围
hibernate的一级缓存中,存储数据必须 持久态数据
Hibernate的缓存分为一级缓存和二级缓存(二级缓存现在不使用了),Hibernate的这两级缓存都位于持久化层,存储的都是数据库数据的备份。其中第一级缓存为Hibernate的内置缓存,不能被卸载。接下来围绕Hibernate的一级缓存进行详细的讲解。
Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的Java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应的数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate一级缓存的作用就是减少对数据库的访问次数。
在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存。只要Session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。所以一级缓存也被称为Session的基本缓存。
Hibernate的一级缓存有如下特点:
- 当应用程序调用Session接口的
save()
、update()
、saveOrUpdate()
时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。 - 当调用Session接口的
load()
、get()
方法,一级Query接口的list()
、iterator()
方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,再去数据库中查询相应的对象,并添加到一级缓存中。 - 当调用Session的
close()
方法时,Session缓存会被清空。
测试一级缓存
1 | // 证明Hibernate一级缓存的存在 |
以上代码中,第一次执行Session的get()方法获取customer1对象时,由于一级缓存中没有数据,所以Hibernate会向数据库发送一条sql语句,查询id为1的对象;当再次调用Session的get()方法获取customer2对象时,不会再发送sql语句,这是因为customer2对象是从一级缓存中获取的。
当Session对象的生命周期还没有结束的时候,在查询相同的数据对象,会现在缓存之中寻找,如果有找到就直接使用缓存中的数据.

接下来验证一下代码的执行结果是否和描述的一致。在Customer customer1 = session.get(Customer.class, 1l);这一行设置断点,用debug方式执行该方法,程序进入断点后点击单步跳过(F6),代码执行过System.out.println(customer1);语句后,控制台的输出结果如下:

从上图的输出结果可以看出,customer2对象的查询结果被直接打印了,说明第二次调用Session对象的get()方法时,没有向数据库发送select语句,而是直接从一级缓存中获取customer2对象。
之前我们介绍过,Hibernate的持久态对象能够自动更新数据库,其实就是依赖了一级缓存。那么一级缓存为什么就可以去更新数据库呢?其实是因为一级缓存的一块特殊区域——快照区。

HIbernate向一级缓存放入数据时,同时复制一份数据放入到Hibernate的快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果这两个对象中的属性发送变化,则执行update语句,将缓存中的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。
1 | // 一级缓存中的快照区:持久态对象能够自动更新数据库 |
Hibernate Session
Session 概述
Session 是什么
- 持久化管理器:负责执行所有持久化操作的核心接口
- 短生命周期对象:通常一个业务操作对应一个Session
- 一级缓存:维护了持久化对象的缓存
- 工作单元:代表应用程序与数据库的一次会话
Session 概述
Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口,它提供了基本的保存,更新,删除和加载Java对象的方法
Session 具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应。Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷新缓存(flush)。也叫一级缓存。
在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存。 只要 Session 实例没有结束生命周期, 且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期。Session 缓存可减少 Hibernate 应用程序访问数据库的频率。
session缓存的示例
1
2
3
4
5
6
7
public void getUser(){
User user=session.get(User.class,1);
User user2=session.get(User.class,1);
System.out.println(user);
System.out.println(user2);//查询出来的两条记录完全一样
}站在持久化的角度,Hibemate把对象分为4种状态:持久化态,临时状态,游离状态,删除状态,Session的特定方法能使对象从一个状态转换到另一个状态.
Session 核心方法
对象状态管理方法
方法 | 说明 | 对象状态变化 |
---|---|---|
save() |
将临时对象持久化 | 临时 → 持久 |
persist() |
同save(),但无返回值 | 临时 → 持久 |
get() |
立即加载对象 | 无 → 持久 |
load() |
延迟加载对象 | 无 → 代理(持久) |
update() |
更新脱管对象 | 脱管 → 持久 |
merge() |
合并脱管对象状态 | 脱管 → 持久 |
delete() |
删除对象 | 持久 → 删除 |
saveOrUpdate() |
智能保存或更新 | 临时/脱管 → 持久 |
evict() |
从缓存移除对象 | 持久 → 脱管 |
clear() |
清空整个缓存 | 所有持久 → 脱管 |
flush() |
同步缓存与数据库 | 保持状态 |
查询方法
方法 | 说明 |
---|---|
createQuery() |
创建HQL查询 |
createSQLQuery() |
创建原生SQL查询 |
createCriteria() |
创建Criteria查询(已废弃) |
byId() |
通过ID加载(5.2+) |
事务控制方法
方法 | 说明 |
---|---|
beginTransaction() |
开始事务 |
getTransaction() |
获取当前事务 |
缓存管理方法
evict()
:移除特定对象
1 | session.evict(student); // 从缓存移除该对象 |
clear()
:清空整个缓存
1 | session.clear(); // 清空所有缓存对象 |
contains()
:检查对象是否在缓存
1 | boolean cached = session.contains(student); |
session缓存的操作方法

flush方法:
session会按照缓存中对象属性的变化来更新数据库中的记录,使数据库中记录和缓存中的对象保持一致,默认情况下,在以下时间点会刷新缓存:
当应用程序调用
Transaction.commit()
方法时,该方法会先调用flush()方法,然后在 向 数据库提交事务显示的调用flush()方法时
当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先 flush 缓存,以保证查询结果能够反映持久化对象的最新状态
flush 缓存的例外情况:
如果对象使用 native 生成器生成 OID, 那么当调用 Session 的 save() 方法保存对象时, 会立即执行向数据库插入该实体的 insert 语句.
flush()和commit()方法的区别:
flush()方法会执行一系列的sql语句,但是不会提交事物;commit()在提交事物前,会先调用flush()方法,然后再提交事物,提交事物以为这将数据库的操作永久的保存下来
测试flush方法
1 |
|
可以发现当调用了修改了缓存之中的对象,在调用了flush()方法之后,会执行update语句以来保证缓存之中的数据和数据库之中的数据保持同步

数据库之中保存的数据

refresh:
refresh会强制发送select
语句,
以使数据库中的记录和缓存中的对象保持一致;如果在调用refresh方法前,手动的修改数据库中的记录,查询出来的结果还不是最新的,这跟数据库的数据的隔离级别是相关的,可以在配置文件中显示的修改事物的隔离级别,每一个隔离级别都对应一个整数;
1 |
|
在暂停的时候修改数据库中的info数据

但是会发现最后输出的结果并不是修改过的数据,因为可重复读的隔离级别,事务持续期间,禁止其他事务对这个字段进行更新。

缓存同步机制
自动同步:在以下时机会自动flush:
- 事务提交时
- 执行查询前(确保查询结果准确)
- 显式调用
session.flush()
手动同步:
1 | session.flush(); // 强制同步缓存与数据库 |
evict()
从session缓存中把指定的持久化对象移除
1 |
|

Session核心方法详解
对实体类 CURD 操作
添加操作
通过调用session
里面的save
方法实现
save()方法最大的特点就是将一个临时对象变为持久化对象
为对象分配ID
在flush()缓存时会发送一条INSERT语句.
1 |
|

- 在save()执行前,设置ID的方法是无效的,在save()方法执行过后修改id,会出现异常
1 |
|

持久化对象的ID是不能够被修改的

通过调用 presist()
方法
persist()方法也会执行INSERT操作,与save()类似,但无返回值
在presist()方法执行之前,如果对象已经有ID值,则不会执行INSERT,而会抛出一个异常.
1 |
|

与save()的区别:
不保证立即执行INSERT(可能在flush时执行)
不返回ID,需通过对象获取
JPA规范方法,行为更标准化
查询操作
调用session里面的get方法实现
执行get()方法会立即加载对象,session.get(实体类的class, id值)
是根据
id 查询
1 |
|
特点:
立即发出SELECT语句
对象不存在时返回null
返回真实对象(非代理)
调用session里面的load方法实现
延迟加载,返回代理对象
执行load方法,若不使用该对象,则不会立即执行查询操作,而返回一个代理对象.
1 | // 返回代理对象,不立即查询 |
可以发现使用load()
方法得到的对象在没有使用的情况下是一个代理对象,这是因为使用load()
方法得到的对象,在没有使用任何属性的情况下,不会立即加载,而是加载到内存之中,在有需要的时候再加载对象.
- 延迟加载(访问非ID属性时查询)
- 对象不存在时抛
ObjectNotFoundException
- 返回代理对象(运行时生成子类)
若查询一个数据表中没有的记录,而且Session也没有被关闭,同时需要使用对象的时候.
- 使用get方法查询会:返回null
- 使用load方法查询:若不是用该对象的任何属性没有问题,若需要初始化,抛出异常.
在需要初始化代理对象之前若关闭Session. load方法可能会抛出 异常
1 |
|
抛出的异常org.hibernate.LazyInitializationException: could not initialize proxy - no Session
(无法初始化代理,没有Session)

更新操作
自动脏检查更新
最常用方式:Hibernate自动检测变化
1 | Student student = session.get(Student.class, 1L); |
Session跟踪持久化对象的状态
flush时比较快照,生成UPDATE语句
调用session里面的update方法实现
首先查询,修改值,然后调用 update 显式更新脱管对象
update()方法相当于sql语句之中的Update语句

若更新一个持久化对象,不必显示的调用update()方法,因为在调用Transaction
的commit()
方法时,会调用session的flush()
更新一个游离对象,需要显示的调用update()方法,可以将一个游离对象转换为持久化对象
当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常
当 update() 方法关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常.
注意:
- 无论要更新的游离对象是否与数据表中的记录是否一致,都会发送Update语句.
1 |
|

如何能让update方法不再盲目的触发update语句?在.hbm.xml文件的class节点设置一个select-before-update=“true”()但通常不使用该属性
1 | <class name="News" table="NEWS" select-before-update="true"> |
如果数据表中没有对应的记录,但还调用了update方法,会抛出异常
当update()方法关联一个游离对象时,如果在Session的缓存之中已经存在了相同的OID的持久化对象.会出现异常,因为在Session缓存中,不能够同时存在两个OID相同的对象,也就是说 同ID对象已存在于Session时会抛异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void testUpdate(){
//使用get()方法得到一个持久化对象,此时这个对象会保存在session的缓存之中
News vo = (News) this.session.get(News.class,1);
//创建一个于vo的id相同的对象
News temp = new News();
temp.setId(vo.getId());
//使用update()方法关联这个temp对象
//由于此时的temp对象和vo对象的id都是相同的
//并且vo对象已经保存在了session之中
//所以当使用update()方法的时候,就会出现在同一session之中存在两个id相同的对象
this.session.update(temp);
}
应确保对象所有属性已设置(否则可能覆盖为null)
调用session里面的merge方法实现
更安全的更新方式
将脱管对象的状态复制到持久化对象中
1 | Student detachedStudent = new Student(); |
方法工作流程
- 检查传入对象:
- 如果为 null,直接返回 null
- 如果是持久化状态,直接返回该对象(无操作)
- 查找对应持久化对象:
- 在 Session 缓存中查找同 ID 的实体
- 如果找到,将脱管对象属性值复制到该持久化对象
- 如果未找到,从数据库加载或创建新实例
- 返回值处理:
- 返回的是持久化对象(可能是缓存中的或新创建的)
- 重要:传入的参数对象不会被关联到 Session
- 状态同步:
- 在 flush 时,生成的持久化对象会同步到数据库
merge() 与 update() 的对比
特性 | merge() | update() |
---|---|---|
同ID对象存在时 | 合并属性值到持久化对象 | 抛出NonUniqueObjectException |
返回值 | 返回持久化对象 | 无返回值 |
参数对象状态 | 保持脱管状态 | 转为持久化状态 |
SQL执行时机 | 根据flush策略 | 根据flush策略 |
级联行为 | 支持CascadeType.MERGE | 支持CascadeType.UPDATE/SAVE_UPDATE |
新对象处理 | 可能转为save操作 | 抛出TransientObjectException |
性能 | 可能需要额外SELECT | 直接操作 |
调用session里面的saveOrUpdate方法实现
Session 的 saveOrUpdate() 方法同时包含了 save() 与 update() 方法的功能,自动判断保存或更新

1 | // 新对象(无ID) - 执行INSERT |
判断对象是不是临时对象:
Java 对象的 OID 为 null
映射文件中为
<id>
设置了 unsaved-value 属性, 并且 Java 对象的 OID 取值与这个 unsaved-value 属性值匹配
判断逻辑:
- 对象ID为null → save()
- 对象ID非null → update()
如果OID不为null,但数据表中还没有和其对应的记录,会抛出一个异常.
1 |
|

删除操作
调用session中的delete()
方法
删除持久化对象,只要OID和数据表中的一条记录对应,就执行delete操作
如果OID和数据库中的一条记录保持一致,则执行删除操作,把对象从 Session 缓存中删除, 该对象进入删除状态,若OID在数据库中没有对应的记录,则抛出异常,Hibernate 的 cfg.xml 配置文件中有一个 hibernate.use_identifier_rollback 属性, 其默认值为 false, 若把它设为 true,改变 delete() 方法的运行行为: delete() 方法会把持久化对象或游离对象的 OID 设置为 null, 使它们变为临时对象,软删除
1 | Student student = session.get(Student.class, 1L); |
注意事项:
- 删除后对象变为Removed状态
- 关联对象需考虑级联删除
- 批量删除建议使用HQL更高效
Session 方法的完整例子
使用 spring data jpa
添加依赖
1 | <dependencies> |
应用配置 (application.yml)
1 | spring: |
定义实体类
1 | import javax.persistence.*; |
Repository 接口
1 | import org.springframework.data.jpa.repository.JpaRepository; |
完整 Service 示例
1 | import javax.persistence.EntityManager; |
测试控制器
1 | import org.springframework.web.bind.annotation.*; |
测试用例
1 | import javax.transaction.Transactional; |
Spring Data JPA 与 Hibernate Session 的关系
EntityManager
:JPA 标准接口,Spring Data JPA 底层使用 Hibernate 实现persist()
→ Hibernatesave()/persist()
merge()
→ Hibernatemerge()
find()
→ Hibernateget()
getReference()
→ Hibernateload()
remove()
→ Hibernatedelete()
- Repository 方法:
save()
:实际执行 merge 语义deleteById()
:先查询再删除
事务管理
@Transactional
注解自动管理事务边界- 方法结束时自动提交,异常时回滚
- 同一个事务内共享持久化上下文
乐观锁冲突处理
1 |
|