问题描述
我试图用Spring测试一个实体EJB3。
EJB本身不使用Spring,我想保留生产JPA配置的重复最小化即,不复制persistence.xml例如)。
我的单元测试似乎可行,但即使我的单元测试应该是事务性的,数据仍然会在各种测试方法之间持续存在。 ..
以下是我的实体:
package sample;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Ejb3Entity {
public Ejb3Entity(String data){
super();
this.data = data;
}
私人长ID;
私有字符串数据;
@Id
@GeneratedValue
public Long getId(){
return id;
}
public void setId(Long id){
this.id = id;
}
public String getData(){
return data;
}
public void setData(String data){
this.data = data;
}
}
我的单元测试:
package sample;
导入static org.junit.Assert。*;
导入javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
导入org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {/ appContext.xml})
@Transactional
public class Ejb3EntityTest {
@PersistenceContext
EntityManager em;
@Before
public void setUp()抛出异常{
Ejb3Entity one = new Ejb3Entity(测试数据);
em.persist(one);
$ b @Test
public void test1()抛出异常{
Long count =(Long)em.createQuery(select count(* )from Ejb3Entity)。getSingleResult();
assertEquals(Long.valueOf(1l),count);
$ b @Test
public void test2()抛出异常{
Long count =(Long)em.createQuery(select count(* )from Ejb3Entity)。getSingleResult();
assertEquals(Long.valueOf(1l),count);
}
}
和我的appContext.xml:
<?xml version =1.0encoding =UTF-8?>
< beans xmlns =http://www.springframework.org/schema/beans
xmlns:xsi =http://www.w3.org/2001/XMLSchema-instancexmlns :tx =http://www.springframework.org/schema/tx
xmlns:context =http://www.springframework.org/schema/context
xsi:schemaLocation = http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework。 org / schema / tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http ://www.springframework.org/schema/context/spring-context.xsd>
< bean id =jotmclass =org.springframework.transaction.jta.JotmFactoryBean/>
< bean id =transactionManager
class =org.springframework.transaction.jta.JtaTransactionManager>
< property name =userTransactionref =jotm/>
< property name =allowCustomIsolationLevelsvalue =true/>
< / bean>
< bean id =dataSourceclass =org.enhydra.jdbc.standard.StandardXADataSource>
< property name =driverNamevalue =org.h2.Driver/>
< property name =urlvalue =jdbc:h2:mem:unittest; DB_CLOSE_DELAY = -1/>
< property name =uservalue =/>
< property name =passwordvalue =/>
< property name =transactionManagerref =jotm/>
< / bean>
< bean id =emf
class =org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean>
< property name =persistenceUnitPostProcessors>
< bean class =sample.JtaDataSourcePersistenceUnitPostProcessor>
< property name =jtaDataSourceref =dataSource/>
< / bean>
< / property>
< property name =jpaVendorAdapter>
< bean class =org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter>
< property name =showSqlvalue =false/>
< property name =generateDdlvalue =true/>
< property name =databasevalue =H2/>
< property name =databasePlatformvalue =org.hibernate.dialect.H2Dialect/>
< / bean>
< / property>
< property name =jpaPropertyMap>
< map>
< entry key =hibernate.transaction.manager_lookup_class
value =org.hibernate.transaction.JOTMTransactionManagerLookup/>
< entry key =hibernate.transaction.auto_close_sessionvalue =false/>
< entry key =hibernate.current_session_context_classvalue =jta/>
< / map>
< / property>
< / bean>
< / beans>
当我运行我的测试时,test2失败,因为它找到了2个实体,我预期只有一个第一个应该已经回滚...)
我已经尝试了很多不同的配置,这个似乎是我可以得到的最全面的...我有没有其他的想法。是吗?
我设法使用而不是JOTM。 Bitronix提供了允许非XA数据库参与JTA事务的LrcXADataSource。我认为问题在于H2不符合XA标准,enhydra StandardXADataSource
并不是那么神奇(我也是用HSQLDB结束的,但与问题无关)。
这是我的spring上下文:
<?xml version =1.0encoding =UTF-8?>
< beans xmlns =http://www.springframework.org/schema/beans
xmlns:xsi =http://www.w3.org/2001/XMLSchema-instancexmlns :tx =http://www.springframework.org/schema/tx
xmlns:context =http://www.springframework.org/schema/context
xsi:schemaLocation = http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework。 org / schema / tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http ://www.springframework.org/schema/context/spring-context.xsd>
< context:annotation-config />
< tx:注解驱动的事务管理器=transactionManager/>
< bean id =btmConfigfactory-method =getConfiguration
class =bitronix.tm.TransactionManagerServices>
< property name =serverIdvalue =spring-btm/>
< property name =journalvalue =null/>
< / bean>
< bean id =BitronixTransactionManagerfactory-method =getTransactionManager
class =bitronix.tm.TransactionManagerServicesdepends-on =btmConfig,dataSource
destroy-method =关机/>
< bean id =transactionManager
class =org.springframework.transaction.jta.JtaTransactionManager>
< property name =transactionManagerref =BitronixTransactionManager/>
< property name =userTransactionref =BitronixTransactionManager/>
< property name =allowCustomIsolationLevelsvalue =true/>
< / bean>
<! - 数据源定义 - >
< bean id =dataSourceclass =bitronix.tm.resource.jdbc.PoolingDataSource
init-method =initdestroy-method =close>
< property name =classNamevalue =bitronix.tm.resource.jdbc.lrc.LrcXADataSource/>
< property name =uniqueNamevalue =unittestdb/>
< property name =minPoolSizevalue =1/>
< property name =maxPoolSizevalue =3/>
< property name =allowLocalTransactionsvalue =true/>
< property name =driverProperties>
<道具>
< prop key =driverClassName> org.hsqldb.jdbcDriver< / prop>
< prop key =url> jdbc:hsqldb:mem:unittestdb< / prop>
< prop key =user> sa< / prop>
< prop key =password>< / prop>
< /道具>
< / property>
< / bean>
< bean id =emf
class =org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean>
< property name =dataSourceref =dataSource/>
< property name =jpaVendorAdapter>
< bean class =org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter>
< property name =showSqlvalue =true/>
< property name =generateDdlvalue =true/>
< / bean>
< / property>
< property name =jpaPropertyMap>
< map>
< entry key =hibernate.transaction.manager_lookup_class
value =org.hibernate.transaction.BTMTransactionManagerLookup/>
< entry key =hibernate.transaction.auto_close_sessionvalue =false/>
< entry key =hibernate.current_session_context_classvalue =jta/>
< / map>
< / property>
< / bean>
I am trying to test an entity EJB3 with Spring.
The EJB itself does not uses Spring and I would like to keep duplications of the production JPA configuration minimal (ie not duplicating persistence.xml for exemple).
My unit tests seems to work but even though my unit tests should be transactionnal, data is persisted between the various test methods ...
Here is my entity :
package sample;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Ejb3Entity {
public Ejb3Entity(String data) {
super();
this.data = data;
}
private Long id;
private String data;
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
My unit test :
package sample;
import static org.junit.Assert.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/appContext.xml"})
@Transactional
public class Ejb3EntityTest {
@PersistenceContext
EntityManager em;
@Before
public void setUp() throws Exception {
Ejb3Entity one = new Ejb3Entity("Test data");
em.persist(one);
}
@Test
public void test1() throws Exception {
Long count = (Long) em.createQuery("select count(*) from Ejb3Entity").getSingleResult();
assertEquals(Long.valueOf(1l), count);
}
@Test
public void test2() throws Exception {
Long count = (Long) em.createQuery("select count(*) from Ejb3Entity").getSingleResult();
assertEquals(Long.valueOf(1l), count);
}
}
and my appContext.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction" ref="jotm" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<bean id="dataSource" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:unittest;DB_CLOSE_DELAY=-1" />
<property name="user" value="" />
<property name="password" value="" />
<property name="transactionManager" ref="jotm" />
</bean>
<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitPostProcessors">
<bean class="sample.JtaDataSourcePersistenceUnitPostProcessor">
<property name="jtaDataSource" ref="dataSource" />
</bean>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
<property name="database" value="H2" />
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.JOTMTransactionManagerLookup" />
<entry key="hibernate.transaction.auto_close_session" value="false" />
<entry key="hibernate.current_session_context_class" value="jta" />
</map>
</property>
</bean>
</beans>
When I run my test, test2 fails because it finds 2 entity where I expected only one (because the first one should have been rollbacked ...)
I have tried a lot of different configurations and this one seems to be the most comprehensive I can get ... I have no other ideas. Do you ?
I managed to make it work using Bitronix instead of JOTM. Bitronix provides a LrcXADataSource that allows a non XA database to participate in the JTA transaction.
I think the issues were that H2 is not XA compliant and the enhydra StandardXADataSource
does not make it magically so (I also ended using HSQLDB but that is unrelated to the issue).
Here is my spring context that works :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Bitronix Transaction Manager embedded configuration -->
<bean id="btmConfig" factory-method="getConfiguration"
class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm" />
<property name="journal" value="null" />
</bean>
<!-- create BTM transaction manager -->
<bean id="BitronixTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig,dataSource"
destroy-method="shutdown" />
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="BitronixTransactionManager" />
<property name="userTransaction" ref="BitronixTransactionManager" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<!-- DataSource definition -->
<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource"
init-method="init" destroy-method="close">
<property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource" />
<property name="uniqueName" value="unittestdb" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="3" />
<property name="allowLocalTransactions" value="true" />
<property name="driverProperties">
<props>
<prop key="driverClassName">org.hsqldb.jdbcDriver</prop>
<prop key="url">jdbc:hsqldb:mem:unittestdb</prop>
<prop key="user">sa</prop>
<prop key="password"></prop>
</props>
</property>
</bean>
<!-- Entity Manager Factory -->
<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="database" value="HSQL" />
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.BTMTransactionManagerLookup" />
<entry key="hibernate.transaction.auto_close_session" value="false" />
<entry key="hibernate.current_session_context_class" value="jta" />
</map>
</property>
</bean>
这篇关于Spring / JTA / JPA单元测试:回滚不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!