通过使用SpringJunit4ClassRunner
注释,我试图使用@Transactional
来测试我的DAO类,而不会在完成后留下数据。我的DAO类包含(剥离):
@Repository
public class IdsFunctionJpaController {
@PersistenceContext
EntityManager em;
public void save(IdsFunction function) {
if (function.getId() == 0) {
create(function);
} else {
update(function);
}
}
@Transactional
private void create(IdsFunction idsFunction) {
try {
em.persist(idsFunction);
}
catch (Exception e) {
System.out.println(e);
} finally {
em.close();
}
}
@Transactional
private void update(IdsFunction function) {
try {
em.merge(function);
} finally {
em.close();
}
}
}
我的首发JUnit测试用例是
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"} )
public class IdsFunctionJpaControllerTest {
@Autowired
IdsFunctionJpaController dao;
@Test
@Transactional
public void addFunction() {
IdsFunction function = new IdsFunction();
function.setDescription("Test Function Description");
dao.save(function);
assertTrue(function.getId() != 0);
}
}
我在这里尝试做的只是测试实体是否已创建,但此测试失败。如果我删除@Transactional
注释,则测试通过,但测试实体仍保留在数据库中。我做错了什么?
此致
答案 0 :(得分:6)
你正在敲打JDK代理。
您的dao.save()
方法是非事务性的,它会尝试调用事务方法create()
和update()
。但事务性事件发生在类外的JDK代理中,而save方法已经在类中。
请参阅this previous answer of mine以供参考。
<强>解决方案:强>
save()
方法具有交易性<强>参考:强>
更新:我被Dao方法中存在令人困惑的@Transactional
注释所误导。你应该删除它们,它们什么也不做,让人迷惑。
正如您在上面发布的链接中所读到的那样,@Transactional
注释只有在公共方法中出现时才会生效。这些方法将从Spring bean外部调用(所以你不能在一个方法中使用一个方法)委托给同一个类的一个或多个代理方法的类。
Spring为事务测试提供了特殊的支持类,如9.3.5.4 Transaction management中所述。如果让测试类从AbstractTransactionalJUnit4SpringContextTests
扩展,则在每次测试后都会得到自动事务回滚。在大多数情况下,这正是您所需要的。
答案 1 :(得分:5)
您需要刷新会话。通常这发生在交易结束时,这就是为什么你通常只需要在测试中担心它。
将EntityManager
注入测试类并在保存后调用em.flush()
。
此外,您的DAO图层不应该是事务性的。交易通常只在服务层有意义。
事实上,你的DAO也是完全错误的,这个测试无法显示。这些事务注释将无效,因为它们是内部方法调用。你也不应该自己关闭EntityManager
- 容器(Spring)会为你做这件事。此外,不要捕获泛型Exception
,当你不做时,不要只记录并忽略它们。异常应该传播到应该正确处理它们的服务层。另外,不要打印到stdout,使用适当的日志框架。