我将JPA与Spring一起使用,并在测试中保存一个实体。在编写测试以验证一个实体与另一个实体的关系是否正确设置的过程中,我遇到了一个经常遇到的问题。我有一个测试方法(设置为回滚):
- 创建实体
- 保存实体
- Flushes
- 检索实体
- 验证实体
问题是,当我查看Hibernate日志时,我只看到数据库的一个插入,我希望在那里看到一个插入然后是一个选择。
我知道这是因为Hibernate试图为我节省一些时间,并且知道它有我试图检索的ID的实体,但这绕过了一个重要的步骤:我想确保实体真的进入了数据库,并且看起来像我认为应该的那样。为了测试实体是否真的在数据库中,处理这个问题的最佳方法是什么?
注意:我认为这涉及到以某种方式分离实体或告诉Hibernate清除其缓存,但当我只能访问JpaRepository对象时,我不确定如何做到这一点。
部分代码:
public interface UserRepository extends JpaRepository<User, Long> {
//...
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JpaConfig.class, // JpaConfig just loads our config stuff
loader = AnnotationConfigContextLoader.class)
@TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {
@Test
@Transactional
public void testRoles() {
User user = new User("name", "email@email.com");
// eventually more here to test entity-to-entity relationship
User savedUser = userRepository.save(user);
userRepository.flush();
savedUser = userRepository.findOne(savedUser.getId());
Assert.assertNotNull(savedUser);
// more validation here
}
}
您有两种策略:
- 绕过任何JPA缓存发出本地SQL查询
- 确保在重新加载之前清除持久性上下文
对于(1),您可以更改测试以扩展以下Spring类,该类除了在每个测试的开始/结束时自动开始/回滚事务外,还将允许您访问可用于发布本机SQL的Spring JdbcTemplate。
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.html
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/jdbc/core/simple/SimpleJdbcTemplate.html
对于(2),您可以通过执行以下操作来清除持久性上下文(其中EntityManagerFactory被注入到您的测试中:
EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory).clear();
请参阅下面的基本测试类,我通常使用它并演示上面的内容,它还允许在每次测试之前(通过DBUnit)用已知数据填充数据库。
https://github.com/alanhay/spring-data-jpa-bootstrap/blob/master/src/test/java/uk/co/certait/spring/data/repository/AbstractBaseDatabaseTest.java
(事实上,在上面我实际上是通过注入数据源创建一个新的JdbcTemplate。记不清为什么了…)
您基本上想要测试Hibernate的功能,而不是自己的代码。我的第一个建议是:不要这么做!它已经经过多次测试和验证。
如果你真的想测试它,有几个选项:
- 执行查询(而不是get。查询将被执行(您应该在日志中看到它),并解释结果。您取回的对象仍然是您保存的对象,因为它在会话中
- 您可以从会话中逐出对象,然后重新获取它。如果您使用
SessionFactory.getCurrentSession()
,您将获得与存储库使用的季节相同的季节。有了它,你就可以收回对象