Spring集成测试不回滚
我正在使用 Spring + Hibernate + H2。我在集成测试中执行数据库操作(通过调用服务类)。我希望 Spring 在每个测试方法之后回滚更改,但我无法让它工作。起初我使用MySQL(带有MyISAM,不支持事务),但是换成H2后问题仍然存在。我尝试了几个数据源定义(在阅读它必须是 XA 感知之后),但似乎没有任何帮助。
我使用 http://code.google.com/p/generic-dao/对于我的服务层中的 DAO 类和 @Transactional-annotations(如果删除它们,我会收到以下错误:javax.persistence.TransactionRequiredException:没有事务正在进行)。
我的测试类如下所示:
@ContextConfiguration("classpath:...spring.xml")
@Transactional
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class DemoServiceTest extends AbstractTestNGSpringContextTests{
@Autowired
private DemoService demoService;
@Test
public void addTest()
{
int size = demoService.findAll().size();
Test coach = new Test();
test.setName("Test");
testService.save(test);
Assert.assertEquals(size+1,testService.findAll().size());
}
}
这是我的 spring 配置:
<!-- <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"> -->
<!-- <property name="driverClassName" value="org.h2.Driver" /> -->
<!-- <property name="url" value="jdbc:h2:~/test" /> -->
<!-- <property name="username" value="sa" /> -->
<!-- <property name="password" value="" /> -->
<!-- </bean> -->
<!-- <bean id="myDataSource" class=" com.mchange.v2.c3p0.ComboPooledDataSource"> -->
<!-- <property name="driverClass" value="org.h2.Driver" /> -->
<!-- <property name="jdbcUrl" value="jdbc:h2:~/test" /> -->
<!-- <property name="user" value="sa" /> -->
<!-- <property name="password" value="" /> -->
<!-- </bean> -->
<bean id="myDataSource" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean">
<property name="uniqueResourceName" value="test" />
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/test" />
<property name="user" value="sa" />
<property name="password" value="" />
<property name="maxPoolSize" value="20" />
<property name="reapTimeout" value="300" />
</bean>
<bean id="demoDAO" class="...DemoDAOImpl" />
<bean id="demoService" class="...DemoServiceImpl" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="test" />
<property name="persistenceXmlLocation" value="classpath:persistence.xml" />
<property name="dataSource" ref="myDataSource" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaVendorAdapter" ref="vendorAdapter" />
</bean>
<bean id="vendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="H2" />
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="searchProcessor" class="com.googlecode.genericdao.search.jpa.JPASearchProcessor">
<constructor-arg ref="metadataUtil" />
</bean>
<bean id="metadataUtil"
class="com.googlecode.genericdao.search.jpa.hibernate.HibernateMetadataUtil"
factory-method="getInstanceForEntityManagerFactory">
<constructor-arg ref="entityManagerFactory" />
</bean>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven />
这是我的服务实现:
@Transactional
public class TestServiceImpl implements TestService {
private TestDAO<Test,Long> dao;
@Autowired
public void setDao(TestDAO<Test,Long> dao) {
this.dao = dao;
}
public void save(Test test) {
dao.persist(test);
dao.flush();
}
public List<Test> findAll() {
return dao.findAll();
}
public Test findByName(String name) {
if (name == null)
return null;
return dao.searchUnique(new Search().addFilterEqual("name", name));
}
}
编辑: dao.persist() 方法基本上封装了从 genericdao 对以下方法的调用(em 返回当前的 EntityManager):
/**
* <p>
* Make a transient instance persistent and add it to the datastore. This
* operation cascades to associated instances if the association is mapped
* with cascade="persist". Throws an error if the entity already exists.
*
* <p>
* Does not guarantee that the object will be assigned an identifier
* immediately. With <code>persist</code> a datastore-generated id may not
* be pulled until flush time.
*/
protected void _persist(Object... entities) {
for (Object entity : entities) {
if (entity != null)
em().persist(entity);
}
}
一切正常,但更改仍保留在数据库中。有什么想法吗?
I'm using Spring + Hibernate + H2. I do database operations in my integration tests (by calling a service class). I want Spring to rollback the changes after each test method, but I can't get it to work. At first I used MySQL (with MyISAM, which doesn't support transaction), but after changing to H2 the problem remains. I tried several DataSource-definitions (after reading that it must be XA-aware), but nothing seems to help.
I use http://code.google.com/p/generic-dao/ for my DAO-classes and @Transactional-annotations in my service layer (if I remove them, I get the following error: javax.persistence.TransactionRequiredException: no transaction is in progress).
My test classes look like this:
@ContextConfiguration("classpath:...spring.xml")
@Transactional
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
public class DemoServiceTest extends AbstractTestNGSpringContextTests{
@Autowired
private DemoService demoService;
@Test
public void addTest()
{
int size = demoService.findAll().size();
Test coach = new Test();
test.setName("Test");
testService.save(test);
Assert.assertEquals(size+1,testService.findAll().size());
}
}
Here's my spring-configuration:
<!-- <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"> -->
<!-- <property name="driverClassName" value="org.h2.Driver" /> -->
<!-- <property name="url" value="jdbc:h2:~/test" /> -->
<!-- <property name="username" value="sa" /> -->
<!-- <property name="password" value="" /> -->
<!-- </bean> -->
<!-- <bean id="myDataSource" class=" com.mchange.v2.c3p0.ComboPooledDataSource"> -->
<!-- <property name="driverClass" value="org.h2.Driver" /> -->
<!-- <property name="jdbcUrl" value="jdbc:h2:~/test" /> -->
<!-- <property name="user" value="sa" /> -->
<!-- <property name="password" value="" /> -->
<!-- </bean> -->
<bean id="myDataSource" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean">
<property name="uniqueResourceName" value="test" />
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/test" />
<property name="user" value="sa" />
<property name="password" value="" />
<property name="maxPoolSize" value="20" />
<property name="reapTimeout" value="300" />
</bean>
<bean id="demoDAO" class="...DemoDAOImpl" />
<bean id="demoService" class="...DemoServiceImpl" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="test" />
<property name="persistenceXmlLocation" value="classpath:persistence.xml" />
<property name="dataSource" ref="myDataSource" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaVendorAdapter" ref="vendorAdapter" />
</bean>
<bean id="vendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="H2" />
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="searchProcessor" class="com.googlecode.genericdao.search.jpa.JPASearchProcessor">
<constructor-arg ref="metadataUtil" />
</bean>
<bean id="metadataUtil"
class="com.googlecode.genericdao.search.jpa.hibernate.HibernateMetadataUtil"
factory-method="getInstanceForEntityManagerFactory">
<constructor-arg ref="entityManagerFactory" />
</bean>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven />
And that's my service implementation:
@Transactional
public class TestServiceImpl implements TestService {
private TestDAO<Test,Long> dao;
@Autowired
public void setDao(TestDAO<Test,Long> dao) {
this.dao = dao;
}
public void save(Test test) {
dao.persist(test);
dao.flush();
}
public List<Test> findAll() {
return dao.findAll();
}
public Test findByName(String name) {
if (name == null)
return null;
return dao.searchUnique(new Search().addFilterEqual("name", name));
}
}
EDIT: The dao.persist()-method basically encapsulates the call to the following method from genericdao (em returns the current EntityManager):
/**
* <p>
* Make a transient instance persistent and add it to the datastore. This
* operation cascades to associated instances if the association is mapped
* with cascade="persist". Throws an error if the entity already exists.
*
* <p>
* Does not guarantee that the object will be assigned an identifier
* immediately. With <code>persist</code> a datastore-generated id may not
* be pulled until flush time.
*/
protected void _persist(Object... entities) {
for (Object entity : entities) {
if (entity != null)
em().persist(entity);
}
}
Everything works fine, but the changes remain in the database. Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为您需要从 AbstractTransactionalTestNGSpringContextTests 而不是 AbstractTestNGSpringContextTests 扩展。
I think you need to extend from AbstractTransactionalTestNGSpringContextTests instead of AbstractTestNGSpringContextTests.