use*_*452 7 spring hibernate sessionfactory transactional transactionmanager
使用Spring + Hibernate和事务注释.
我正在尝试测试以下内容:
@Transactional服务方法来持久化它我遇到的第一个问题是在步骤2中读取User对象,只返回Hibernate 1级缓存中的那个,并且实际上没有从数据库中读取.
因此,我使用Session手动从缓存中驱逐对象以强制从数据库读取.但是,当我这样做时,对象值永远不会在单元测试中持久存在(我知道在测试完成后它会因为我指定的设置而回滚).
我尝试在调用@Transactionalservice方法后手动刷新会话,并且DID提交更改.但是,这不是我的预期.我认为@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());
}
}
Run Code Online (Sandbox Code Playgroud)
如果我手动刷新会话,则测试成功.但是,我本来希望该@Transactional方法可以处理提交和刷新会话.
updateUserSamePassword的服务方法如下:
@Transactional("userTransactionManager")
@Override
public void updateUserSamePassword(User user) {
userDAO.updateUser(user);
}
Run Code Online (Sandbox Code Playgroud)
DAO方法在这里:
@Override
public void updateUser(User user) {
Session session = sessionFactory.getCurrentSession();
session.update(user);
}
Run Code Online (Sandbox Code Playgroud)
SesssionFactory是自动装配的:
@Autowired
@Qualifier("userSessionFactory")
private SessionFactory sessionFactory;
Run Code Online (Sandbox Code Playgroud)
我正在使用XML应用程序上下文配置.我有:
<context:annotation-config />
<tx:annotation-driven transaction-manager="userTransactionManager" />
Run Code Online (Sandbox Code Playgroud)
和
<bean id="userDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${user.jdbc.driverClass}"/>
<property name="jdbcUrl" value="${user.jdbc.jdbcUrl}" />
<property name="user" value="${user.jdbc.user}" />
<property name="password" value="${user.jdbc.password}" />
<property name="initialPoolSize" value="3" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="17" />
</bean>
<bean id="userSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="userDataSource" />
<property name="configLocation" value="classpath:user.hibernate.cfg.xml" />
</bean>
<bean id="userTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="userDataSource" />
<property name="sessionFactory" ref="userSessionFactory" />
</bean>
Run Code Online (Sandbox Code Playgroud)
还有一个组件扫描服务和dao类.正如我所说,这是在生产中.
我认为如果我在该方法@Transactional的末尾标记了一个方法(例如这里的更新方法),Spring会强制Session提交并刷新.
我只能看到几个选项:
我错误配置了一些东西,尽管这对我来说很有用(只是不是单元测试).任何猜测?有关如何测试的任何想法?
关于单元测试配置本身的一些东西不像应用程序那样.
事务和会话不能像那样工作.我唯一的推论是Spring在调用该更新方法后离开事务和/或会话打开.因此,当我在Session对象上手动逐出用户时,尚未提交这些更改.
任何人都可以确认这是否是预期的行为?不@Transaction应该强制提交和刷新会话?如果没有,那么如何测试标记的方法@Transactional并且方法实际上与事务一起工作?
即,我应该如何在这里重写我的单元测试?
还有其他想法吗?
这就是我遇到的问题.在测试方法中考虑以下代码:
String testDisplayNameChange = "ThisIsATest";
User user = userService.readUser(new Long(77));
user.setDisplayName(testDisplayNameChange);
user = userService.readUser(new Long(77));
assertNotEquals(user.getDisplayName(), testDisplayNameChange);
Run Code Online (Sandbox Code Playgroud)
请注意,方法userService.readUser在服务类中标记为@Transactional.
如果该测试方法标记为@Transactional,则测试失败.如果不是,那就成功了.现在我不确定Hibernate缓存是否/何时实际涉及.如果测试方法是事务性的,那么每次读取都发生在一个事务中,我相信它们只能访问Hibernate 1级缓存(并且实际上并未从数据库中读取).但是,如果测试方法不是事务性的,那么每次读取都会在它自己的事务中发生,并且每个读取都会触及数据库.因此,休眠级别1高速缓存与会话/事务管理相关联.
拿走:
即使测试方法在另一个类中调用多个事务方法,如果该测试方法本身是事务性的,那么所有这些调用都发生在一个事务中.测试方法是"工作单元".但是,如果测试方法不是事务性的,那么对该测试中的事务方法的每次调用都在它自己的事务中执行.
我的测试类被标记为@Transactional,因此每个方法都是事务性的,除非标记为覆盖注释,如@AfterTransaction.我可以很容易地标记类@Transactional并标记每个方法@Transactional
使用Spring @Transactional时,Hibernate 1级缓存似乎与事务有关.即后续读取同一事务中的对象将命中hibernate 1级缓存而不是数据库.请注意,您可以调整二级缓存和其他机制.
我打算使用@Transactional测试方法,然后在测试类中的另一个方法上使用@AfterTransaction,并提交原始SQL来评估数据库中的值.这将完全绕过ORM和hibernate 1级缓存,确保您比较DB中的实际值.
简单的答案是从我的测试类中取出@Transactional.好极了.
| 归档时间: |
|
| 查看次数: |
6247 次 |
| 最近记录: |