Java Persistence API (JPA) 之 EntityManager

EntityManager 是 Java Persistence API (JPA) 中的核心接口之一,它是应用程序与持久化层(即数据库)之间进行交互的主要入口点。EntityManager 提供了一系列方法,用于执行与对象-关系映射(ORM)相关的操作,如创建、读取、更新、删除(CRUD)实体对象,以及执行查询等。以下是 EntityManager 接口的主要职责和功能:

  • 实体管理:

persist(): 将一个新创建的实体对象添加到持久化上下文中,标记为待保存状态。当事务提交时,该实体将被保存到数据库中。
merge(): 更新一个已在数据库中存在的实体对象,或者将一个游离(detached)状态的实体重新与持久化上下文关联并同步其状态。
remove(): 从持久化上下文中移除一个实体,并在事务提交时将其从数据库中删除。
find(): 根据给定的实体类和主键值从数据库中加载一个实体对象到持久化上下文。

  • 查询操作:

createQuery(): 创建一个基于JPA Criteria API的查询对象,用于执行复杂的类型安全查询。
createNamedQuery(): 根据预定义的命名查询(在实体类或XML元数据中声明)创建一个查询对象。
createNativeQuery(): 创建一个基于原生SQL查询的对象,返回结果可以映射到实体类或基本类型。

  • 事务管理:

EntityManager 与 Transaction 接口紧密协作,提供对数据库事务的支持。通常通过 EntityManager.getTransaction() 获取当前事务,并使用 begin(), commit(), rollback() 等方法进行事务控制。

  • 缓存管理:

EntityManager 内部维护了一级缓存(Persistence Context),它是一个对象的集合,代表了当前与数据库的一致视图。对实体的操作首先发生在一级缓存中,只有在特定时刻(如事务提交)才会与数据库进行同步。

  • 锁定与版本控制:

提供方法如 lock() 用于对实体进行悲观或乐观锁定,以防止并发修改冲突。
支持实体的版本属性管理,自动检测并处理并发更新问题。

  • 其他辅助功能:

flush(): 强制将当前持久化上下文中所有未同步的更改立即写入数据库。
clear(): 清除持久化上下文中的所有实体,释放资源。
close(): 关闭 EntityManager 实例,释放与数据库的连接。
通常情况下,EntityManager 由 EntityManagerFactory 创建,并在每次操作完成后关闭或由依赖注入框架(如Spring)管理其生命周期。在实际应用中,推荐使用依赖注入和Spring Data JPA的JpaRepository、CrudRepository等更高层次的抽象来操作 EntityManager,以简化开发并更好地融入Spring生态系统。
        总结来说,EntityManager 是 JPA 提供的一个关键接口,它封装了与数据库交互的所有必要操作,使得开发者可以通过面向对象的方式来操作数据库,而无需直接编写SQL。通过合理使用 EntityManager,可以实现对数据库的高效、类型安全且易于维护的访问。

让我们对 EntityManager 的一些核心功能和使用细节进行更深入的探讨:

实体管理

  • 持久化新实体(persist())

当调用 entityManager.persist(entity) 时,entity 被标记为新创建的、待持久化的状态。这意味着它会被加入到当前 EntityManager 的一级缓存(Persistence Context)中。
实际的数据库插入操作并不立即发生,而是延迟到事务提交时。这样可以批量处理多个插入操作,提高效率。
如果实体已经存在于数据库中(即具有唯一标识符),persist() 方法不会抛出异常,但也不会执行任何操作。对于已存在的实体,应使用 merge() 方法。

  • 更新实体(merge())

entityManager.merge(entity) 用于将一个游离(detached)状态的实体重新与持久化上下文关联,并同步其状态。游离状态的实体是指那些曾经是持久化状态,但由于 EntityManager 关闭或清除等原因不再与任何 EntityManager 关联的实体。
merge() 方法会查找数据库中是否存在与传入实体具有相同标识符的记录。如果存在,将实体的属性值合并到数据库中的记录,并返回一个与当前 EntityManager 关联的实体副本。如果不存在,则相当于执行一次 persist() 操作。
merge() 后返回的实体是持久化上下文中的最新版本,应使用这个返回值进行后续操作,而不是原来的传入实体。

  • 删除实体(remove())

调用 entityManager.remove(entity) 可以从持久化上下文中移除一个实体,并在事务提交时将其从数据库中删除。
删除操作同样发生在事务提交时,如果在事务提交前调用 em.find() 加载同主键的实体,仍能获取到该实体。这是因为删除操作尚未传播到数据库。
如果尝试删除一个未被管理(非持久化状态)的实体,remove() 方法会抛出 IllegalArgumentException。
查询操作
基于Criteria API的查询(createQuery())
entityManager.createQuery(criteraQuery) 使用JPA Criteria API创建一个类型安全的查询对象。Criteria API允许通过程序构造查询条件,适用于动态构建查询的情况。
查询结果可以是实体列表、单个实体或标量值。例如:

  CriteriaBuilder cb = entityManager.getCriteriaBuilder();
  CriteriaQuery<User> cq = cb.createQuery(User.class);
  Root<User> user = cq.from(User.class);
  cq.where(cb.equal(user.get("username"), "john.doe"));
  List<User> users = entityManager.createQuery(cq).getResultList();
  

基于命名查询(createNamedQuery())
entityManager.createNamedQuery(queryName) 根据在实体类或XML元数据中预先定义的命名查询字符串创建查询对象。

命名查询的优势在于将SQL-like查询语句与Java代码分离,提高了可读性和可维护性。例如,在实体类中声明:

  @Entity
  @NamedQueries({
      @NamedQuery(name = "User.findByUsername", query = "SELECT u FROM User u WHERE u.username = :username")
  })
  public class User {
      // ...
  }
  
然后在代码中使用:  
TypedQuery<User> query = entityManager.createNamedQuery("User.findByUsername", User.class);
  query.setParameter("username", "john.doe");
  List<User> users = query.getResultList();
  

原生SQL查询(createNativeQuery())
entityManager.createNativeQuery(sql, resultClass) 或 createNativeQuery(sql, resultSetMapping) 允许直接使用原生SQL语句进行查询。
结果可以映射到实体类或通过ResultSetMapping定义的映射关系。原生查询在需要利用数据库特定功能(如存储过程、特定SQL语法)时非常有用。
事务管理
JPA 本身并不直接管理事务,而是通过与底层JTA(Java Transaction API)或JDBC事务协调来实现事务支持。
通常通过 entityManager.getTransaction() 获取当前事务对象,然后使用 begin(), commit(), rollback() 等方法进行事务控制。例如:

  EntityTransaction tx = entityManager.getTransaction();
  tx.begin();
  try {
      // 执行数据库操作...
      tx.commit();
  } catch (Exception e) {
      tx.rollback();
      throw e;
  }
  

        在Spring环境下,通常推荐使用@Transactional注解或PlatformTransactionManager来管理事务,让Spring容器负责事务的开启、提交和回滚,避免在业务代码中显式处理事务。

  • 缓存管理

EntityManager 内部维护的一级缓存(Persistence Context)是一个对象的集合,它代表了当前与数据库的一致视图。对实体的操作首先发生在一级缓存中,只有在特定时刻(如事务提交)才会与数据库进行同步。
一级缓存的作用包括:
读取缓存:当通过 find()、getReference() 或查询方法加载实体时,如果一级缓存中已有相同标识符的实体,直接返回缓存中的实体,避免重复从数据库查询。
写入缓存:对实体的持久化操作(如 persist()、merge()、remove())先更新一级缓存中的实体状态,延迟到事务提交时再同步到数据库。
实体状态跟踪:管理实体的瞬时(transient)、持久(persistent)、游离(detached)和删除(removed)四种状态,确保实体状态的一致性。
锁定与版本控制

  • 悲观锁定(lock())

entityManager.lock(entity, LockModeType) 用于对实体进行悲观锁定,防止并发修改冲突。
LockModeType.WRITE:在执行查询时立即获取写锁,阻止其他事务读取或修改该实体,直到当前事务结束。
LockModeType.READ:在执行查询时立即获取读锁,阻止其他事务获取写锁,但允许其他事务获取读锁。
悲观锁定可能导致数据库死锁,应谨慎使用,并确保在短时间内完成操作并提交事务。

  • 乐观锁定

JPA支持乐观锁定,通常通过在实体类中添加一个版本字段(如 @Version 注解的 Long version)实现。
当两个事务同时修改同一实体时,提交时会检查版本号是否与开始时一致。如果不一致(即有其他事务修改过该实体),则抛出 OptimisticLockException,提示并发修改冲突。
其他辅助功能

  • 刷新缓存(flush())

entityManager.flush() 强制将当前持久化上下文中所有未同步的更改立即写入数据库。在某些需要立即看到数据库更新结果的情况下(如查询刚修改过的数据),可能需要手动调用此方法。

  • 清理缓存(clear())

entityManager.clear() 清除持久化上下文中的所有实体,释放资源。清除后,所有先前加载的实体变为游离状态,新的查询将从数据库重新加载数据。

  • 关闭 EntityManager(close())

当不再需要 EntityManager 时,应调用 entityManager.close() 来关闭它,释放与数据库的连接。在Spring环境下,通常由容器自动管理 EntityManager 的生命周期,不需要手动关闭。
总结来说,EntityManager 是 JPA 提供的一个强大而灵活的接口,它封装了与数据库交互的所有必要操作,包括实体的生命周期管理、查询执行、事务控制、缓存管理以及并发控制。深入理解和熟练运用 EntityManager 的各项功能,有助于构建高效、健壮且易于维护的持久化层。在实际项目中,结合使用Spring Data JPA的高级抽象(如 JpaRepository)和 EntityManager 的低级API,可以更好地适应不同场景的需求。

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值