难以使用测试代码验证事务内的hibernate更新查询

时间:2014-09-29 17:46:02

标签: spring hibernate jpa testing

我试图为使用直接sql查询在单个事务中更新数据库的hibernate DAO编写我的测试代码的验证部分而感到非常沮丧。

背景:

  • 在Spring 4中使用Hibernate 4.1
  • 交易边界通过@Transactional注释/ JpaTransactionManager
  • 进行控制
  • 我们在很大程度上只是将hibernate用作JPA实现,并避免大多数特定于hibernate的东西;即我们不能直接与Session对象进行交互。

情况:我有一个使用NamedQuery执行直接SQL / HQL更新的DAO。以这种方式执行更新的主要驱动程序(与调用entityManager.persist相反)是分离对象的问题。我不想重新获取对象只是为了坚持保存我的更改。分离对象的原因是因为它被缓存了。

我想编写一个单元测试来调用这个DAO的更新方法,然后调用DAO的find方法从数据库中提取新对象以验证更改是否正确。另一个问题是,我希望在事务(即AbstractTransactionalTestNGSpringContextTests的子类)中运行此单元测试,以便回滚更改并为下一次测试保持状态清洁。

问题的关键:当我在执行更新后调用DAO上的find方法时,Hibernate显然对所发生的更改一无所知。我认为这是由于某些缓存或者hibernate中的某些内容导致它无法由于某种原因而获取这些更改。我试图查找此缓存并使其无效;到目前为止没有成功。

示例:

@Test
public void testUpdate() {
    SomeUser obj = makeSomeUser("joe");

    // update the name using a NamedQuery; does not call `persist`
    dao.updateName(obj.getId(), "bob"); 

    SomeUser found = dao.find(obj.getId());
    // This verification fails, it finds "joe" still
    assertEquals(found.getName(), "bob"); 
}

...别处

public void updateName(long id, String newName) {
    Query query = entityManager.createQuery("UPDATE USERS set NAME = :name WHERE id = :id");
    query.setParameter("name", newName);
    query.setParameter("id", id);
    query.executeUpdate();
}

我过去唯一能解决这个问题的方法是:

  1. 不要在交易中运行测试,然后再执行手动清理。我真的很想避免这种情况,因为在我看来这是不好的做法。
  2. 使用entityManger.createNativeQuery执行验证步骤,该步骤可以检测更新的更改。我想避免这种情况,因为它不一致,最终只是为了测试而重复代码。我想在测试中使用相同的查找代码,就像在其他代码库中一样...
  3. 我错过了一个简单的答案吗?我是否可以以非休眠友好的方式做事?有没有更好的方法来测试休眠DAO的?

2 个答案:

答案 0 :(得分:-1)

如果您将存储库集成测试视为在您的体系结构中占据与您通常注入存储库的服务相同的位置,则可能会有所帮助。考虑到这一点,您可以在测试中放置@Transactional注释(从spring-tx开始),以划分事务边界。这允许测试插入/更新测试数据,并在事务期间检查数据库,并在事务在测试结束时回滚时自动清理所有这些(它将会)。

在我正在研究的系统中,我们所有的集成测试都是使用这种策略编写的。他们都有这种形式:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/integration-test-context.xml")
@Transactional
public class ThingRepositoryIT {...}

我们始终使用@Before开始配置我们需要的数据:

@Autowired
private JdbcTemplate jdbcTemplate;

获取jdbc模板的句柄,该模板可以运行创建测试数据的运行脚本。

另外,我们经常需要在测试中的hibernate会话上调用flush(),以确保在我们的测试中测试之前hibernate已将其缓存数据写入数据库。

答案 1 :(得分:-2)

首先。为什么要费心测试DAO?您应该只关注通过具有一些真正价值和反馈的适当服务进行集成测试。如果您在服务之外进行操作,则不会进行任何交易。

这个updateName函数很奇怪。在JPA中对实体执行更新时,我们在大多数情况下会传入更新的实体并获取我们设置为要更新的实体的新值。 makeSomeUser()方法实际上做了什么?它是否坚持实体?提供更多代码。如果你想测试更新,你需要有一个持久化实体,你必须知道你想要更新什么,通过获取id,基于该id在数据库上执行查找,如果返回匹配的实体然后执行更新操作返回的实体。在交易结束时,所有更改都将保留。你的测试方法在这里是错误的。