JPA 与 Hibernate 核心
概述
Java Persistence API(JPA)是 Jakarta EE 的 ORM 标准接口,Hibernate 是其最主流的实现。本文深入剖析核心机制,包括 EntityManager 接口设计、Persistence Context 生命周期、一级缓存、脏检查、flush 策略以及事件监听器链。
EntityManager 核心接口
EntityManager 是 JPA 最核心的接口,定义了对实体进行 CRUD 操作以及管理 Persistence Context 的全部契约。
接口定义
public interface EntityManager {
void persist(Object entity);
<T> T merge(T entity);
void remove(Object entity);
<T> T find(Class<T> entityClass, Object primaryKey);
<T> T getReference(Class<T> entityClass, Object primaryKey);
void flush();
void refresh(Object entity);
void lock(Object entity, LockModeType lockMode);
void detach(Object entity);
boolean contains(Object entity);
Query createQuery(String qlString);
<T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass);
Query createNativeQuery(String sqlString);
EntityTransaction getTransaction();
void joinTransaction();
<T> EntityGraph<T> createEntityGraph(Class<T> rootType);
void setProperty(String propertyName, Object value);
void close();
boolean isOpen();
<T> T unwrap(Class<T> cls);
}核心方法语义
| 方法 | 语义 |
|---|---|
persist() | 将实体从 transient 转为 managed,纳入当前 Persistence Context |
merge() | 将 detached 实体合并到 Persistence Context,返回 managed 实例 |
remove() | 将 managed 实体标记为删除,SQL DELETE 在 flush 时执行 |
find() | 按主键查找,优先从一级缓存返回,未命中则查询数据库 |
getReference() | 返回仅持有主键的代理对象,访问非主键属性时触发查询 |
flush() | 将 Persistence Context 中的变更同步到数据库 |
detach() | 将实体从 Persistence Context 中脱离 |
contains() | 检查实体是否被当前 Persistence Context 管理 |
获取方式
在 Spring 环境下通过 @PersistenceContext 注入,Spring 为每个事务创建独立的 EntityManager 实例:
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void saveUser(User user) {
entityManager.persist(user);
}
}EntityManager 由 EntityManagerFactory 创建,是线程安全的,在应用启动时创建一次;EntityManager 是非线程安全的,每个事务应使用独立实例。
Persistence Context 生命周期
Persistence Context(持久化上下文)是 JPA 的核心概念,本质上是 Hibernate 一级缓存的 JPA 规范抽象。实体在其中存在四种状态。
四种实体状态
+-----------+
| transient | (瞬态)
+-----+-----+
|
persist() / merge()
v
+-----------+
+------------->| managed |<------------+
| | 托管态 | |
| +-----+-----+ |
| | | |
| find() remove() |
| | | |
| v v |
| +-----------+ +-----------+ |
| | detached | | removed | |
| | 游离态 | | 删除态 | |
| +-----+-----+ +-----------+ |
| | |
+--- merge() |
flush() → DELETE| 状态 | 说明 | 一级缓存 | 数据库记录 |
|---|---|---|---|
| Transient | 刚 new 的对象,未被 EntityManager 管理 | 否 | 否 |
| Managed | 通过 persist() / merge() / find() 纳入管理 | 是 | 可能已存在或尚未 flush |
| Detached | 曾被管理,但上下文已关闭或调用了 detach() | 否 | 是 |
| Removed | 调用了 remove() 但尚未 flush | 是(标记删除) | 是 |
Transient → Managed
User user = new User(); // transient
entityManager.persist(user); // managed实体被管理后,Hibernate 会将其放入一级缓存、创建快照用于脏检查、拦截 setter 调用(字节码增强时),并在事务提交时 flush 变更。
Managed → Detached
User user = entityManager.find(User.class, 1L);
entityManager.detach(user); // detached
// 此后对 user 的修改不会同步到数据库
// 重新关联需使用 merge()
User reManaged = entityManager.merge(user); // managed(返回新实例)merge() 流程:检查 Persistence Context 是否已有同主键 managed 实例 → 有则复制属性到该实例,无则从数据库加载 → 返回 managed 实例。
Managed → Removed
User user = entityManager.find(User.class, 1L);
entityManager.remove(user); // removed
// contains(user) → false,但一级缓存仍保留引用标记为删除
// 实际 DELETE 在 flush 时执行在 removed 状态下,flush 前调用 persist() 可取消删除。
一级缓存(Session Cache)
Hibernate 的一级缓存是强制开启、不可关闭的缓存层,生命周期严格绑定到 Session(即 EntityManager)。
内部结构
一级缓存由 StatefulPersistenceContext 实现,包含以下核心 Map:
StatefulPersistenceContext
├── entitiesByKey // Map<EntityKey, Object> — 实体实例
├── entitySnapshotsByKey // Map<EntityKey, Object[]> — 快照数据
├── entityEntries // Map<EntityKey, EntityEntry> — 实体元信息
├── collectionEntries // Map<CollectionKey, CollectionEntry> — 集合缓存
└── proxiesByKey // Map<EntityKey, Object> — 代理对象StatefulPersistenceContext 核心源码
// org.hibernate.engine.internal.StatefulPersistenceContext
public class StatefulPersistenceContext implements PersistenceContext {
private Map<EntityKey, Object> entitiesByKey;
private Map<EntityKey, Object> entitySnapshotsByKey;
private Map<EntityKey, EntityEntry> entityEntries;
public Object getEntity(EntityKey key) {
return entitiesByKey.get(key);
}
public void addEntity(EntityKey key, Object entity) {
entitiesByKey.put(key, entity);
}
public Object getSnapshot(EntityKey key) {
return entitySnapshotsByKey.get(key);
}
public void addSnapshot(EntityKey key, Object snapshot) {
entitySnapshotsByKey.put(key, snapshot);
}
public void removeEntity(EntityKey key) {
entitiesByKey.remove(key);
entityEntries.remove(key);
entitySnapshotsByKey.remove(key);
}
}缓存键结构
EntityKey 由实体类型和主键值构成:
// org.hibernate.engine.spi.EntityKey
public final class EntityKey implements Serializable {
private final Object identifier; // 主键值
private final int hashCode;
private final EntityPersister persister;
public EntityKey(Object id, EntityPersister persister) {
this.identifier = id;
this.persister = persister;
this.hashCode = generateHashCode(persister, id);
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
EntityKey that = (EntityKey) other;
return persister.equals(that.persister)
&& identifier.equals(that.identifier);
}
}缓存工作流程
find() 的查找路径:一级缓存 → 二级缓存(可选)→ 数据库。
User user1 = entityManager.find(User.class, 1L); // 查库,写入缓存
User user2 = entityManager.find(User.class, 1L); // 缓存命中,无 SQL
System.out.println(user1 == user2); // true — 同一对象引用生命周期
| 阶段 | 事件 |
|---|---|
| 创建 | EntityManager 创建时(或事务开始),SessionImpl 初始化 StatefulPersistenceContext |
| 填充 | persist()、find()、merge()、查询执行时加入实体 |
| 清空 | flush() 更新快照;clear() 清空所有缓存 |
| 销毁 | EntityManager.close() 或事务结束时释放 |
@Transactional
public void batchInsert(List<User> users) {
for (int i = 0; i < users.size(); i++) {
entityManager.persist(users.get(i));
if (i % 50 == 0) {
entityManager.flush();
entityManager.clear(); // 防止缓存堆积
}
}
}局限性
- 作用域局限:仅在当前
Session内有效,跨 Session 无法共享 - 内存占用:大量查询后不及时
clear()可能导致堆压力 - 无过期策略:没有 TTL 机制,仅随 Session 销毁
脏检查(Dirty Checking)
脏检查是 Hibernate 自动检测 managed 实体属性变更的机制,开发者无需手动调用 update() 即可同步变更。
快照比较机制(Snapshot Comparison)
默认方式。flush 时遍历每个 managed 实体,比较当前值与快照值:
flush() 时对每个 managed 实体:
├── 获取当前属性值
├── 从 entitySnapshotsByKey 获取快照(加载时的值)
├── 逐字段比较
│ ├── 全相同 → 跳过
│ └── 有差异 → 标记为脏,生成 UPDATE
└── 执行 UPDATE SQL// org.hibernate.event.internal.DefaultFlushEntityEventListener
public void onFlushEntity(FlushEntityEvent event) {
EntityEntry entry = event.getEntityEntry();
if (entry.getStatus() == Status.MANAGED) {
Object[] currentState = persister.getPropertyValues(entity);
Object[] snapshot = entry.getLoadedState();
if (snapshot != null) {
int[] dirtyProperties = persister.findDirty(
currentState, snapshot, entity, session);
event.setDirtyProperties(dirtyProperties);
}
}
}字段拦截机制(Dirty Tracking)
通过字节码增强,在 setter 中记录被修改的字段,避免 flush 时全量比较。
// 字节码增强后的实体
@Entity
public class User {
@Id private Long id;
private String name;
public void setName(String name) {
$$_hibernate_trackChange("name", this.name, name);
this.name = name;
}
}增强后的实体实现 SelfDirtinessTracker 接口:
public interface SelfDirtinessTracker {
boolean $$_hibernate_hasDirtyAttributes();
String[] $$_hibernate_getDirtyAttributes();
void $$_hibernate_clearDirtyAttributes();
}在 Maven 中通过 hibernate-enhance-maven-plugin 启用:
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<configuration>
<enableDirtyTracking>true</enableDirtyTracking>
</configuration>
</plugin>DefaultFlushEntityEventListener 优先使用字段拦截判断:
private boolean requiresDirtyCheck(FlushEntityEvent event) {
if (entity instanceof SelfDirtinessTracker) {
return ((SelfDirtinessTracker) entity)
.$$_hibernate_hasDirtyAttributes();
}
return true; // 回退到快照比较
}两种机制对比
| 维度 | 快照比较 | 字段拦截 |
|---|---|---|
| 生效条件 | 无需配置 | 需要字节码增强 |
| 性能 | flush 时遍历所有字段 | 仅在 setter 调用时记录 |
| 内存 | 保存完整快照数组 | 仅保存脏属性名称数组 |
| 兼容性 | 所有 Hibernate 版本 | Hibernate 5.2+ |
内部实现
// org.hibernate.persister.entity.AbstractEntityPersister
public int[] findDirty(Object[] currentState, Object[] previousState,
Object entity, SessionImplementor session) {
return TypeHelper.findDirty(
this.propertyTypes, currentState, previousState,
propertyUpdateability, session);
}
// org.hibernate.type.TypeHelper
public static int[] findDirty(Type[] types, Object[] current,
Object[] previous, boolean[] checkable, SessionImplementor session) {
List<Integer> dirtyIndices = new ArrayList<>();
for (int i = 0; i < types.length; i++) {
if (checkable[i] && types[i].isDirty(
previous[i], current[i], session)) {
dirtyIndices.add(i);
}
}
return toIntArray(dirtyIndices);
}Flush 时机
Flush 将 Persistence Context 的变更同步到数据库。Hibernate 通过 FlushMode / FlushModeType 控制触发时机。
FlushMode 枚举
| 模式 | 说明 |
|---|---|
FlushModeType.AUTO | JPA 默认,查询前如果存在待同步变更则 flush |
FlushModeType.COMMIT | 仅在事务提交或显式 flush 时同步 |
FlushMode.ALWAYS | Hibernate 特有,每次查询前都 flush |
FlushMode.MANUAL | 仅手动 flush,绝不自动触发 |
AUTO 模式
entityManager.setFlushMode(FlushModeType.AUTO);
User user = entityManager.find(User.class, 1L);
user.setName("Updated");
// JPQL 查询前自动 flush,生成 UPDATE
entityManager.createQuery(
"select u from User u where u.name like :name",
User.class)
.setParameter("name", "Updated%")
.getResultList();AUTO 保证同一事务中查询总能读到之前已做的变更。
COMMIT 模式
entityManager.setFlushMode(FlushModeType.COMMIT);
User user = entityManager.find(User.class, 1L);
user.setName("Updated");
// 查询前不会自动 flush,数据库中仍是旧值
entityManager.createQuery("select u from User u", User.class)
.getResultList();
// 只有显式 flush 或事务提交时才同步
entityManager.flush();源码分析:自动 flush 触发条件
// org.hibernate.internal.SessionImpl
protected void autoFlushIfRequired(Set<EntityPersister> querySpaces) {
if (flushMode == FlushModeType.AUTO
|| flushMode == FlushMode.ALWAYS) {
if (!getActionQueue().areInsertionsOrDeletionsQueued()
&& !getPersistenceContext().hasNonFlushedChanges()) {
return; // 无待同步变更,跳过
}
if (getActionQueue().areTablesToBeUpdated(querySpaces)) {
flush(); // 关联表有 pending 变更,执行 flush
}
}
}flush 执行流程
flush()
├── prepare() — 查找删除实体,解析级联删除
├── preFlush() — 生成主键(IDENTITY/SEQUENCE),触发 @PreUpdate/@PreRemove
├── flushEntities() — 遍历 managed 实体,执行脏检查,注册更新操作
├── flushCollections() — 处理集合变更
├── performExecutions() — 按序执行 SQL(INSERT → UPDATE → DELETE)
└── postFlush() — 更新快照,清除已删除实体的缓存ActionQueue 执行顺序设计为先 insert → 再 update → 最后 delete,以规避外键约束冲突:
// org.hibernate.engine.spi.ActionQueue
public void executeActions() {
executeInsertions(); // INSERT
executeActions(updates); // UPDATE
executeActions(collectionRemovals); // DELETE collections
executeActions(collectionUpdates); // UPDATE collections
executeActions(collectionCreations);// INSERT collections
executeDeletions(); // DELETE
}AbstractEntityManagerImpl.persist() 源码分析
persist() 的调用链跨越三个层次:
AbstractEntityManagerImpl.persist()
→ SessionImpl.persist()
→ DefaultPersistEventListener.onPersist()AbstractEntityManagerImpl.persist()
// org.hibernate.jpa.internal.AbstractEntityManagerImpl
@Override
public void persist(Object entity) {
checkOpen();
try {
internalPersist(entity);
} catch (RuntimeException e) {
throw convert(e);
}
}
private void internalPersist(Object entity) {
Session session = getSession();
session.persist(entity); // 委托给 Hibernate Session
}SessionImpl.persist() → 事件分发
// org.hibernate.internal.SessionImpl
@Override
public void persist(String entityName, Object object) {
firePersist(new PersistEvent(entityName, object, this));
}
private void firePersist(PersistEvent event) {
PersistEventListener[] listeners = listeners(PersistEvent.class);
for (PersistEventListener listener : listeners) {
listener.onPersist(event);
}
}DefaultPersistEventListener.onPersist()
// org.hibernate.event.internal.DefaultPersistEventListener
@Override
public void onPersist(PersistEvent event) {
onPersist(event, new IdentityHashMap<>());
}
public void onPersist(PersistEvent event,
IdentityMap<Object, Object> createdAlready) {
final Object entity = event.getObject();
final EventSource source = event.getSession();
final EntityPersister persister =
source.getEntityPersister(entityName, entity);
// 幂等性检查:已有 managed 实例则直接返回
final Object existing = source.getPersistenceContext()
.getEntity(persister.getEntityKeyWithNullId(entity, source));
if (existing != null) return;
persist(event, persister, createdAlready);
}核心持久化逻辑
private void persist(PersistEvent event, EntityPersister persister,
IdentityMap<Object, Object> createdAlready) {
final Object entity = event.getObject();
final EventSource source = event.getSession();
// 1. 循环检测:防止双向关联无限递归
if (createdAlready.containsKey(entity)) return;
createdAlready.put(entity, entity);
// 2. 级联保存前:处理 @OneToMany(cascade=PERSIST) 子实体
cascadeBeforeSave(source, persister, entity, createdAlready);
// 3. 生成主键(UUID/SEQUENCE 策略在此分配,IDENTITY 延迟到 insert)
if (persister.getIdentifierGenerator() != null) {
// 主键生成逻辑
}
// 4. 注册到 PersistenceContext
source.getPersistenceContext().addEntity(
persister.getEntityKey(entity, source, null), entity);
// 5. 创建快照
source.getPersistenceContext().addSnapshot(
persister.getEntityKey(entity, source, null),
persister.getPropertyValues(entity));
// 6. 入队 ActionQueue
source.getActionQueue().addAction(
new EntityInsertAction(persister, entity,
persister.getPropertyValues(entity), source));
// 7. 级联保存后处理
cascadeAfterSave(source, persister, entity, createdAlready);
}完整时序
entityManager.persist(user)
│
├── AbstractEntityManagerImpl.internalPersist()
│ └── session.persist(user)
│ └── firePersist(new PersistEvent(user))
│
├── DefaultPersistEventListener.onPersist()
│ ├── 获取 EntityPersister(映射元数据)
│ ├── 幂等性检查
│ ├── 循环检测(IdentityMap)
│ ├── cascadeBeforeSave() → 级联保存关联
│ ├── 生成主键
│ ├── addEntity() → 放入 entitiesByKey
│ ├── addSnapshot() → 保存快照
│ ├── addAction(EntityInsertAction) → 入队
│ └── cascadeAfterSave()
│
└── 此时实体为 managed 状态(INSERT SQL 尚未发送)persist() 后的状态验证
User user = new User();
user.setName("Charlie");
System.out.println(entityManager.contains(user)); // false
entityManager.persist(user);
System.out.println(entityManager.contains(user)); // true
entityManager.flush();
// INSERT INTO user (name) VALUES ('Charlie')IDENTITY 主键的特殊处理
@GeneratedValue(strategy = IDENTITY) 需要 INSERT 后才能获取主键,因此 flush 时会立即执行 INSERT:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
User user = new User();
user.setName("Dave");
entityManager.persist(user);
// ID 此时可能为 null
entityManager.flush();
// INSERT 执行,数据库生成自增 ID
System.out.println(user.getId()); // 现在可获取 IDEventListener 链
Hibernate 的事件系统是其可扩展性的基石。每个持久化操作都通过事件监听器链触发。
事件类型体系
Hibernate 定义了约 20 种事件类型,涵盖实体全部生命周期:
public enum EventType {
PERSIST, PERSIST_ON_UPDATE, SAVE, SAVE_UPDATE,
MERGE, DELETE,
LOAD, REFRESH, GET, INITIALIZE,
FLUSH, FLUSH_ENTITY, FLUSH_COLLECTION,
LOCK, REPLICATE, AUTO_FLUSH, DIRTY_CHECK,
POST_INSERT, POST_UPDATE, POST_DELETE, POST_LOAD,
PRE_INSERT, PRE_UPDATE, PRE_DELETE,
}persist 操作的完整监听器链
persist(user) → 注册到 ActionQueue
flush() 时触发:
├── 1. PreInsertEvent → DefaultPreInsertEventListener
│ └── 触发 JPA @PrePersist 回调
├── 2. INSERT SQL → JDBC PreparedStatement 执行
└── 3. PostInsertEvent → DefaultPostInsertEventListener
├── 更新一级缓存实体条目状态
├── 触发 JPA @PostPersist 回调
└── 清理插入队列JPA 生命周期回调注解
@Entity
public class User {
@Id private Long id;
private String name;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@PrePersist
public void prePersist() {
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
@PostPersist
public void postPersist() {
System.out.println("User persisted with id: " + this.id);
}
@PreUpdate
public void preUpdate() {
this.updatedAt = LocalDateTime.now();
}
@PreRemove
public void preRemove() {
System.out.println("Removing user: " + this.id);
}
@PostLoad
public void postLoad() {
System.out.println("User loaded: " + this.name);
}
}完整的 INSERT 事件监听器链源码
// org.hibernate.event.internal.DefaultFlushEntityEventListener
public void onFlushEntity(FlushEntityEvent event) {
final EntityEntry entry = event.getEntityEntry();
if (entry.getStatus() == Status.SAVING
|| entry.getStatus() == Status.MANAGED) {
int[] dirtyProperties = dirtyCheck(event);
if (dirtyProperties != null || event.hasDirtyCollection()) {
if (entry.getStatus() == Status.MANAGED) {
preUpdate(event);
persister.update(...);
}
}
}
}自定义事件监听器
通过 Spring 的 @EventListener 注解注册:
@Component
public class AuditEventListener {
@EventListener @Order(1)
public void onPreInsert(PreInsertEvent event) {
Object entity = event.getEntity();
if (entity instanceof Auditable) {
((Auditable) entity).setCreatedAt(LocalDateTime.now());
}
}
@EventListener @Order(2)
public void onPostInsert(PostInsertEvent event) {
if (event.getEntity() instanceof Auditable) {
auditLogger.log("INSERT", event.getEntity());
}
}
}通过 Hibernate Integrator SPI 注册:
public class CustomIntegrator implements Integrator {
@Override
public void integrate(Metadata metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
EventListenerRegistry registry = serviceRegistry
.getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_INSERT)
.appendListener(new CustomPreInsertListener());
}
}PartialFlush 优化
Hibernate 在 autoFlushIfRequired() 中检查待操作表是否与查询涉及的表有交集,而非无条件 flush:
private Set<EntityPersister> getQueriesToFlush(
Set<EntityPersister> querySpaces,
Map<EntityPersister, ActionQueue> flushOperations) {
Set<EntityPersister> affected = new HashSet<>();
for (EntityPersister persister : flushOperations.keySet()) {
if (querySpaces.contains(persister)) {
affected.add(persister);
}
}
return affected;
}综合示例
展示 Persistence Context、脏检查、flush 和事件回调的协同工作:
@Component
public class JpaDemoService {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void demonstrateCoreMechanisms() {
// 阶段 1: Persist — transient → managed
User user = new User();
user.setName("Alice");
System.out.println(entityManager.contains(user)); // false
entityManager.persist(user);
System.out.println(entityManager.contains(user)); // true
// 此时一级缓存有 user,但尚未 INSERT
// 阶段 2: Flush — 发送 INSERT
entityManager.flush();
// 阶段 3: 脏检查 — 修改属性后 flush 自动 UPDATE
user.setName("Alice Updated");
entityManager.flush();
// 阶段 4: 缓存命中
User sameUser = entityManager.find(User.class, user.getId());
System.out.println(sameUser == user); // true — 一级缓存同一实例
// 阶段 5: Detach
entityManager.detach(user);
System.out.println(entityManager.contains(user)); // false
// 阶段 6: Merge — detached → managed
User mergedUser = entityManager.merge(user);
System.out.println(entityManager.contains(mergedUser)); // true
// 阶段 7: Remove
entityManager.remove(mergedUser);
// 事务提交时 flush → DELETE → @PreRemove / @PostRemove 回调
}
}最佳实践总结
一级缓存相关
- 批量操作时周期性清空缓存:每 50-100 条调用
flush()+clear() - 避免长事务中的缓存膨胀:大量读取时使用
clear()或拆分为短事务 - 利用缓存避免 N+1:关联查询前先查找目标实体,利用缓存减少 SQL
脏检查相关
- 选择恰当策略:读多写少用字段拦截,写密集用快照比较
- 避免不必要检查:无需更新的属性使用
@Column(updatable = false) - 只读事务跳过:
@Transactional(readOnly = true)下直接跳过
Flush 相关
- 理解 AUTO 的代价:复杂查询中可能触发不期望的 flush,考虑 COMMIT 模式
- 显式 flush 获取主键:IDENTITY 策略下 flush 后可获取主键
flush()不是提交:flush 仅推送变更到数据库,仍可回滚
事件监听器相关
- 不要在
@PrePersist中修改关联关系:可能导致不可预测的行为 - 自定义监听器保持轻量:同步执行,耗时操作应异步处理
- 利用监听器实现横切关注点:审计日志、数据同步等适合通过事件监听器实现
参考资源
- Jakarta Persistence 3.1 Specification
- Hibernate ORM 6.x 源码(
org.hibernate包) - Spring Data JPA 3.x 参考文档