如何测试Spring@Transactional,而不只是点击hibernate 1级缓存或手动刷新会话?
使用Spring+Hibernate和事务性注释 我正在尝试测试以下内容:如何测试Spring@Transactional,而不只是点击hibernate 1级缓存或手动刷新会话?,spring,hibernate,transactional,sessionfactory,transactionmanager,Spring,Hibernate,Transactional,Sessionfactory,Transactionmanager,使用Spring+Hibernate和事务性注释 我正在尝试测试以下内容: 调用更改用户对象的方法,然后调用@Transactional服务方法来持久化它 从数据库中读回对象,并确保方法完成后其值正确无误 我遇到的第一个问题是在步骤2中读取用户对象,它只是返回了Hibernate 1级缓存中的一个对象,并没有实际从数据库中读取 因此,我使用会话从缓存中手动逐出该对象,以强制从数据库中读取。但是,当我这样做时,对象值永远不会在单元测试中持久化(我知道,由于我指定的设置,它会在测试完成后回滚) 在调
@Transactional
服务方法来持久化它@Transactional
服务方法之后,我尝试手动刷新会话,但确实提交了更改。然而,这并不是我所期望的。我认为@Transactional
服务方法可以确保在事务返回之前提交事务并刷新会话。
我知道Spring通常会决定何时进行这种管理,但我认为@Transactional
方法中的“工作单元”就是这种方法
在任何情况下,现在我都在试图弄清楚如何测试@Transactional
方法
下面是一个失败的junit测试方法:
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@TransactionConfiguration(transactionManager = "userTransactionManager", defaultRollback = true)
@WebAppConfiguration()
@ContextConfiguration(locations = { "classpath:test-applicationContext.xml",
"classpath:test-spring-servlet.xml",
"classpath:test-applicationContext-security.xml" })
public class HibernateTest {
@Autowired
@Qualifier("userSessionFactory")
private SessionFactory sessionFactory;
@Autowired
private UserService userService;
@Autowired
private PaymentService paymentService;
@Autowired
private QueryService queryService;
@Autowired
private NodeService nodeService;
@Autowired
private UserUtils userUtils;
@Autowired
private UserContext userContext;
@Test
public void testTransactions() {
// read the user
User user1 = userService.readUser(new Long(77));
// change the display name
user1.setDisplayName("somethingNew");
// update the user using service method that is marked @Transactional
userService.updateUserSamePassword(user1);
// when I manually flush the session everything works, suggesting the
// @Transactional has not flushed it at the end of the method marked
// @Transactional, which implies it is leaving the transaction open?
// session.flush();
// evict the user from hibernate level 1 cache to insure we are reading
// raw from the database on next read
sessionFactory.getCurrentSession().evict(user1);
// try to read the user again
User user2 = userService.readUser(new Long(77));
System.out.println("user1 displayName is " + user1.getDisplayName());
System.out.println("user2 displayName is " + user2.getDisplayName());
assertEquals(user1.getDisplayName(), user2.getDisplayName());
}
}
如果手动刷新会话,则测试成功。但是,我希望@Transactional
方法负责提交和刷新会话
updateUserSamePassword的服务方法如下:
@Transactional("userTransactionManager")
@Override
public void updateUserSamePassword(User user) {
userDAO.updateUser(user);
}
@Override
public void updateUser(User user) {
Session session = sessionFactory.getCurrentSession();
session.update(user);
}
DAO方法如下:
@Transactional("userTransactionManager")
@Override
public void updateUserSamePassword(User user) {
userDAO.updateUser(user);
}
@Override
public void updateUser(User user) {
Session session = sessionFactory.getCurrentSession();
session.update(user);
}
SessionFactory是自动连接的:
@Autowired
@Qualifier("userSessionFactory")
private SessionFactory sessionFactory;
我正在使用XML应用程序上下文配置。我有:
<context:annotation-config />
<tx:annotation-driven transaction-manager="userTransactionManager" />
及
还对服务和dao类进行了组件扫描。正如我所说,这在生产中起作用
我认为,如果我有一个标记为@Transactional
的方法,在该方法结束时(例如,这里的update方法),Spring将强制会话提交并刷新
我只能看到几个选项:
@Transaction
不应该在会话上强制提交和刷新吗?如果不是,那么如何测试标记为@Transactional
的方法,以及这些方法是否实际用于事务
也就是说,我应该如何在这里重写我的单元测试
还有其他想法吗?Q
有人能确认这是否是预期的行为吗?不应该
@事务已在会话上强制提交和刷新?如果没有,怎么办
有人会测试标记为@Transactional的方法吗
实际处理事务
A
。spring感知的事务单元测试支持通过设计在测试完成后回滚事务。这是故意的
每个测试(每个带有@test的方法)都会创建一个隐式事务边界,一旦测试完成,就会执行回滚
这样做的结果是,在所有测试完成后,没有实际更改的数据。这就是我们的目标,更像“单元”,而不是像“集成”。你应该阅读这篇文章来解释为什么这是有利的
如果您真的想测试持久化的数据并查看事务边界之外的数据,我建议您进行一种更为端到端的测试,如selenium测试,或者如果您有外部WS/REST API,则点击它
Q
也就是说,我应该如何在这里重写我的单元测试
A
您的单元测试不起作用,因为您没有刷新。逐出将导致更改不同步,即使您已在事务和休眠批处理操作中调用了
session.update
。对于原始SQL,这一点更为明显,因为hibernate延迟持久化以批处理所有操作,因此它会等待会话刷新或关闭,以便与数据库通信以提高性能。如果您session.flush
,尽管SQL将立即执行(即更新将真正发生),然后您可以退出,如果您想强制重新读取,但您可以在事务中重新读取。实际上,我很确定flush
会导致逐出,因此没有必要调用execute
来强制重新读取,但我可能错了。以下是我遇到的问题。在测试方法中考虑这个代码:
String testDisplayNameChange = "ThisIsATest";
User user = userService.readUser(new Long(77));
user.setDisplayName(testDisplayNameChange);
user = userService.readUser(new Long(77));
assertNotEquals(user.getDisplayName(), testDisplayNameChange);
请注意,方法userService.readUser在服务类中标记为@Transactional
如果该测试方法标记为@Transactional,则测试失败。如果不是,它就会成功。现在我不确定Hibernate缓存是否/何时真正参与其中。如果测试方法是事务性的,那么每次读取都发生在一个事务中,我相信它们只会命中Hibernate 1级缓存(实际上不会从数据库中读取)。但是,如果测试方法不是事务性的,那么每次读取都发生在它自己的事务中,并且每次读取都会命中数据库。因此,hibernate 1级缓存与会话/事务管理绑定
外卖: