红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 专栏
  3. Spring Boot 学习笔记
  4. 正文

JPA 学习笔记 2:API

2025年10月5日 8点热度 0人点赞 0条评论

本文的所有示例都将在 Junit 单元测试中完成,因此需要先添加 Junit 依赖:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.14.0</version>
    <scope>test</scope>
</dependency>

这里我的 Idea 可能是因为版本缘故,不支持最新的 Junit 6.0,所以使用的是 Junit 5。

Persistence

如前文展示的,使用Persistence可以获取EntityManagerFactory的实例:

@Cleanup EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpa-demo");

createEntityManagerFactory方法需要接收一个持久单元名(Persistence Unit Name),对应的是 JPA 配置文件src/main/resources/META-INF/persistence.xml中的persistence-unit标签的name属性:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="jpa-demo" transaction-type="RESOURCE_LOCAL">
        <!-- ... -->
    </persistence-unit>
</persistence>

createEntityManagerFactory方法有另一个重载版本:

Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.show_sql", false);
@Cleanup EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpa-demo", properties);
Assertions.assertNotNull(entityManagerFactory);

通过参数properties可以设置 JPA 的配置信息,比如是否打印 SQL 日志(hibernate.show_sql)。

EntityManagerFactory

EntityManagerFactory用于创建EntityManager:

EntityManager entityManager = entityManagerFactory.createEntityManager();

EntityManager

getTransaction

EntityManger可以用于开启事务:

transaction = entityManager.getTransaction();
transaction.begin();

find

使用主键查询数据:

Customer customer = entityManager.find(Customer.class, 1);
System.out.println("-----------------------");
System.out.println(customer);

需要注意的是,执行find后,JPA 会立即执行 SQL 并获取到数据实体,这点和getReference有所不同。

getReference

Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println("-----------------------");
System.out.println(customer);

getReference同样可以根据主键查询数据,但区别在于getReference返回的实际上是一个代理对象,该方法执行时不会立即执行 SQL 语句,而是在真正读取代理对象的相关属性时才执行 SQL 并获取数据。这点有点像 Spring 的 @Lazy 注解标记的 Bean,是一种懒加载行为。

这种方式查询数据有一个弊端,如果在真正执行 SQL 获取数据前 EntityManager 被关闭,就会出现异常:

Customer customer = entityManager.getReference(Customer.class, 1);
transaction.commit();
entityManager.close();
System.out.println("-----------------------");
Assertions.assertThrowsExactly(LazyInitializationException.class, () -> {
    System.out.println(customer);
});

persist

使用 persist 可以保存实例:

Customer customer = new Customer();
customer.setLastName("icexmoon");
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
entityManager.persist(customer);

需要注意的是,如果实体已经存在主键,使用persist保存实体会报错:

Customer customer = new Customer();
customer.setLastName("icexmoon");
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setId(11L);
Assertions.assertThrowsExactly(EntityExistsException.class, () -> {
    entityManager.persist(customer);
});

remove

remove方法可以从数据库中移除实体对应的数据行,需要注意的是,与 MyBatis 不同,它不能移除游离状态(和 Session 不关联)的实体:

Customer customer = new Customer();
customer.setId(1L);
Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> {
    entityManager.remove(customer);
});

只能移除持久态(和 Session 关联)的实体:

Customer customer = entityManager.find(Customer.class, 1);
entityManager.remove(customer);

merge

merge方法的用途类似于 Hibernate 的saveOrUpdate,不过在部分细节有所不同。根据接收的对象状态的不同,merge方法的行为有所不同,可以用下面的流程图表示:

image-20251005154741439

示例1, merge 接收的是一个临时实体(没有 ID):

Customer customer = new Customer();
customer.setLastName("icexmoon");
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
Customer merge = entityManager.merge(customer);
// 临时实体的 ID 依然是 null
Assertions.assertNull(customer.getId());
// merge 方法返回的是一个持久实体,ID 不为 null
Assertions.assertNotNull(merge.getId());

示例2,merge接收的是一个游离实体,且数据库中存在该实体:

Customer customer = new Customer();
customer.setId(3L);
customer.setLastName("Tom");
Customer merge = entityManager.merge(customer);
Assertions.assertEquals("Tom", merge.getLastName());
Assertions.assertEquals(3L, merge.getId());

示例3,merge接收的是一个游离实体,且EntityManager中有 ID 相同实体的缓存:

Customer customer = new Customer();
customer.setId(3L);
customer.setLastName("Jim");
Customer customerExists = entityManager.find(Customer.class, 3L);
Customer merge = entityManager.merge(customer);
Assertions.assertEquals("Jim", merge.getLastName());
Assertions.assertEquals(3L, merge.getId());
Assertions.assertSame(customerExists, merge);

实例4,merge接收的是一个游离实体,且该实体不存在于数据库中:

Customer customer = new Customer();
customer.setId(99L);
customer.setLastName("Bruce");
Assertions.assertThrowsExactly(OptimisticLockException.class, ()->{
    entityManager.merge(customer);
});

教程中说此时会保存数据,但实际测试发现会报错,抛出一个OptimisticLockException异常,可能是 JPA 版本不同导致的。

flush

flush方法用于同步持久上下文环境,即将持久上下文环境中的所有未保存实体的状态信息保存到数据库:

Customer customer = entityManager.find(Customer.class, 3L);
customer.setLastName("Li Lei");
entityManager.flush();

示例中的flush方法调用时,会执行 UPDATE 语句修改数据库中对应的行数据。

可以通过setFlushMode方法修改 flush 的执行方式:

  • FlushModeType.AUTO:自动更新数据库实体

  • FlushModeType.COMMIT:直到下次事务提交才更新数据库记录

可以通过getFlushMode方法获取当前持久上下文环境的 Flush 模式。

refresh

示例:

Customer customer = entityManager.find(Customer.class, 3L);
customer = entityManager.find(Customer.class, 3L);

这个示例执行了两次find,但实际只会执行一次 SELECT 语句,因为 Hibernate 有缓存。

如果需要立即从数据库更新实体数据,可以使用refresh:

Customer customer = entityManager.find(Customer.class, 3L);
customer = entityManager.find(Customer.class, 3L);
// 这里会强制执行 SELECT 更新实体数据
entityManager.refresh(customer);

clear

清除持久上下文环境,断开所有关联的实体。如果这时还未提交的更新会被撤销。

contains

判断一个实体是否属于当前持久上下文环境管理的实体。

Customer customer = entityManager.find(Customer.class, 3L);
Assertions.assertTrue(entityManager.contains(customer));
entityManager.clear();
Assertions.assertFalse(entityManager.contains(customer));

isOpen

判断当前实体管理器是否打开状态。

close

关闭实体管理器。如果实体管理器关联的事务仍然处于活动状态,close调用时不会立即关闭,直到事务执行完毕。

EntityTransaction

EntityTransaction代表 JPA 中的事务,可以执行事务相关的操作:

  • begin,开启事务

  • commit,提交事务

  • rollback,回滚事务

  • setRollbackOnly,将事务设置成只能回滚

  • getRollbackOlny,查看事务是否设置成了只能回滚

  • isActive,查看是否是否活动状态,只有非活动状态才能 begin,只有活动状态才能执行 commit、rollback 等操作。

本文的完整示例可以从这里获取。

参考资料

  • 尚硅谷jpa开发教程全套完整版

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: jpa
最后更新:2025年10月5日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号