JPA测试中内存H2数据丢失及EntityManager共享问题咨询
JPA测试:内存H2数据丢失与EntityManager共享问题解析
一、内存H2测试间数据丢失的原因
H2内存数据库默认是会话私有实例:当使用jdbc:h2:mem:无名称的URL时,每个JDBC连接都会创建独立的内存库实例。JUnit的测试方法默认独立执行,第一个测试结束后,对应的连接关闭,内存库实例直接销毁;第二个测试拿到的是全新的内存库,自然读不到之前的数据。
而文件存储的H2(jdbc:h2:file:./h2data/demodb)是将数据持久化到磁盘,所有连接共享同一个文件实例,因此测试间数据能保留。
二、解决内存H2测试间数据共享的方法
修改内存H2的连接URL,指定数据库名称并添加DB_CLOSE_DELAY=-1参数:
<property name="hibernate.connection.url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1"/>
mem:testdb:指定内存库名称,让所有连接指向同一个实例DB_CLOSE_DELAY=-1:禁止H2在最后一个连接关闭时销毁数据库,确保测试间实例不被回收
三、测试间共享EntityManager的正确方式
EntityManager本身线程不安全,不建议直接跨测试方法共享。更合理的是共享EntityManagerFactory,并通过JUnit生命周期注解控制资源:
优化后的测试类
package com.caveatemptor.core.data.inheritance; import com.caveatemptor.core.data.entites.Tesla; import jakarta.persistence.*; import org.junit.jupiter.api.*; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; public class TeslaTest { private static EntityManagerFactory entityManagerFactory; private EntityManager entityManager; @BeforeAll static void initFactory() { entityManagerFactory = Persistence.createEntityManagerFactory("in.memory.test"); } @BeforeEach void initEntityManager() { entityManager = entityManagerFactory.createEntityManager(); } @AfterEach void closeEntityManager() { if (entityManager.isOpen()) { entityManager.close(); } } @AfterAll static void destroyFactory() { if (entityManagerFactory.isOpen()) { entityManagerFactory.close(); } } @Test void teslaOne(){ Tesla tesla = new Tesla(); tesla.setVehicle("Model X"); EntityTransaction transaction = entityManager.getTransaction(); try { transaction.begin(); entityManager.persist(tesla); transaction.commit(); Tesla teslaV = entityManager.find(Tesla.class, tesla.getID()); assertEquals(teslaV.getVehicle(), "Model X"); } finally { if(transaction.isActive()){ transaction.rollback(); } } } @Test void teslaTwo(){ EntityTransaction transaction = entityManager.getTransaction(); try { transaction.begin(); TypedQuery<Tesla> query = entityManager.createQuery("select t from Tesla t", Tesla.class); List<Tesla> items = query.getResultList(); assertEquals(1, items.size()); } finally { if(transaction.isActive()){ transaction.rollback(); } } } }
@BeforeAll:全局初始化EntityManagerFactory,所有测试共享同一个工厂实例@BeforeEach:每个测试前创建新的EntityManager,保证线程安全@AfterEach:每个测试后关闭EntityManager,释放资源@AfterAll:所有测试结束后销毁EntityManagerFactory
测试隔离可选方案
如果不需要测试间共享数据,可将hibernate.hbm2ddl.auto设为create-drop,让每个测试方法自动重建数据库:
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
内容的提问来源于stack exchange,提问作者Romillion
相关产品推荐
相关产品推荐

