Spring @ Transactional 어노테이션을 사용하는 시스템에 대한 베어 니스 미니멀리스트 JUnit 테스트를 작성하려고하는데 많은 성공을 거두지 못했습니다.Spring @ Transactional
고유 제한 조건이있는 열에 대해 동일한 값을 사용하여 두 개의 인스턴스를 만듭니다. 두 인스턴스 생성이 다른 트랜잭션에서 발생하면 첫 번째 트랜잭션이 커밋되고 두 번째 트랜잭션이 예외를 throw하여 한 행으로 이어질 것으로 예상합니다. 동일한 트랜잭션에서 두 개의 삽입이 발생하면 둘 다 원자 단위로 롤백 될 것으로 예상됩니다. 어딘가에 구성 문제가 있다고 확신하지만, 운이 좋지는 않습니다.
테스트에는 하나 또는 두 개의 인스턴스를 만드는 메소드가있는 Bean (ContextTestHelperImpl/ContextTestHelper)이 있습니다.
@Override
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW,
rollbackFor = {ContextDuplicationException.class})
public Foo createOneFoo(int val) throws ContextDuplicationException {
try {
return fooDAO.createFoo(val);
} catch (Throwable th) {
throw new ContextDuplicationException(th);
}
}
@Override
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW,
rollbackFor = {ContextDuplicationException.class})
public Foo[] createTwoFoos(int val) throws ContextDuplicationException {
try {
return new Foo[] { fooDAO.createFoo(val), fooDAO.createFoo(val) };
} catch (Throwable th) {
throw new ContextDuplicationException(th);
}
}
내가 하나 개 JUnit 테스트 (하여 ContextTest)가 두 번 첫 번째 방법을 호출 : 예상대로이 작동
public void dupTestTwoTransactions() {
contextTestHelper.deleteAllFoo();
Assert.assertEquals(0, contextTestHelper.getCountOfFoo());
boolean hadException = false;
try {
contextTestHelper.createOneFoo(DUPLICATE_VALUE);
} catch (ContextDuplicationException th) {
hadException = true;
System.out.println("[ContextTest][dupTestTwoTransactions] UNEXPECTED ERROR: " + th);
th.printStackTrace();
}
Assert.assertEquals(hadException, false);
Assert.assertEquals(1, contextTestHelper.getCountOfFoo());
try {
contextTestHelper.createOneFoo(DUPLICATE_VALUE);
} catch (ContextDuplicationException th) {
hadException = true;
}
Assert.assertEquals(hadException, true);
Assert.assertEquals(1, contextTestHelper.getCountOfFoo());
}
각각의 방법에 Propagation.REQUIRES_NEW 주석이 있습니다. 첫 번째 호출은 예외를 throw하지 않습니다. 두 번째 호출이 수행합니다. 그것의 끝 부분에는 Foo 테이블에 하나의 행이 있습니다.
내가 한 번 후자의 방법 호출 같은 클래스에 두 번째 JUnit 테스트가 있습니다@Test
public void dupTestOneTransaction() {
contextTestHelper.deleteAllFoo();
Assert.assertEquals(0, contextTestHelper.getCountOfFoo());
boolean hadException = false;
try {
contextTestHelper.createTwoFoos(DUPLICATE_VALUE);
} catch (ContextDuplicationException th) {
hadException = true;
}
Assert.assertEquals(hadException, true);
Assert.assertEquals(0, contextTestHelper.getCountOfFoo());
}
이 두 번째 테스트는 최종 주장 실패 - 난 동안 푸 인스턴스의 수는, 1 0을 기대하고 있습니다.
코드가 JBoss에서 실행될 때 JNDI 조회를 사용하려고하기 때문에 데이터 소스 설정에 약간의 헛소리가 있습니다. 여기
@BeforeClass
public static void setUpClass() throws NamingException {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-test.xml");
DataSource testDataSource = (DataSource) context.getBean("testDataSource");
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("java:comp/env/jdbc/dataSource", testDataSource);
builder.activate();
}
는 넷빈즈에서 테스트 패키지 '기본 패키지에서 설정 내 스프링 test.xml의 파일입니다 : 그 결과, JUnit을이 장면 뒤에 JNDI 룩업 (ContextTest.java)를 설정해야 :
<?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-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder location="user-specific.properties"/>
<bean id="testDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${db.driver.class}</value></property>
<property name="url"><value>${db.url}</value></property>
<property name="username"><value>${db.user}</value></property>
<property name="password"><value>${db.password}</value></property>
</bean>
</beans>
첫 번째 테스트가 작동 한 이후 분명히 데이터베이스에 연결할 수 있으므로 여기에 특별한 문제가 있다고 생각하지 않습니다.
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd"
default-autowire="byName" >
<context:annotation-config />
<context:component-scan base-package="com.xyzzy" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:user-specific.properties</value>
</property>
</bean>
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/dataSource"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="fooDAO" class="com.xyzzy.FooDAOImpl" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="packagesToScan" value="com.xyzzy" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${db.dialect}</prop>
<prop key="hibernate.show_sql">${db.show_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${db.hbm2ddl.auto}</prop>
</props>
</property>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="${db.dialect}"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="true"/>
</bean>
</property>
<property name="packagesToScan" value="com.xyzzy" />
</bean>
</beans>
의 모든되지-전적으로를위한 테스트 클래스 (푸, FooDAO, FooDAOImpl) 패키지 com.xyzzy에, 그리고 테스트들 모두를 (하여 ContextTest : 여기
은 applicationContext.xml이다 , ContextTestHelper, ContextTestHelperImpl, ContextDuplicationException)가 com.xyzzy.test에 있습니다.Foo, FooDAO 또는 FooDAOImpl에는 @Transactional 어노테이션이 없습니다. ContextTestHelperImpl은 트랜잭션 경계를 지정하는 유일한 객체입니다.
정상적으로 동작하도록 수정하는 방법에 대한 제안 사항은 무엇입니까? (dataSource 클래스 또는 transactionManager를 선택하는 데 문제가 있습니까? applicationContext.xml의 일부 설정이 일관성이 없거나 중복되어 있습니까?)
UPDATE :
DAO 구현 클래스 :
또한 Propagation.REQUIRED (스레드와 연관된 트랜잭션이 아니라면 조기에 예외가 발생됩니다 선가와 그것을 시도@Repository("FooDAO")
public class FooDAOImpl implements FooDAO {
private HibernateTemplate hibernateTemplate;
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
@Override
public Foo createFoo(int val) {
Foo foo = new Foo();
foo.setVal(val);
saveFoo(foo);
return foo;
}
void saveFoo(Foo foo) {
hibernateTemplate.save(foo);
}
[... and many similar methods ...]
),하지만 그것의 행동을 변경하지 않습니다.
답장을 보내 주셔서 감사합니다.하지만 문제가 아닌 것으로 확신합니다. 각 방법에 대한 항목/종료를 인쇄 할 때 테스트가 순차적으로 실행되는 것으로 보입니다. (당신이 제안한대로 두 번째 테스트를 다른 고유 한 값을 사용하도록 변경했으며, 여전히 같은 방식으로 실패했습니다.) – user3207820
응답 해 주셔서 감사합니다. 나는 전제에 도전 할 것이다 :
return new Foo[] { fooDAO.createFoo(val), fooDAO.createFoo(val) };
는 동일한 거래에 2 개의 삽입을 수행한다. 그게 당신 DAO에서 발생하지 않을까요 ?? like :entityManager.persist(s); entityManager.persist(s);
. 전제를 테스트하기 위해 디버거를 통해 코드를 실행합니다. 데이터베이스는 두 번째 삽입을 거부합니다. 따라서 전적으로 2 개의 삽입에 대한 전제는 단일 트랜잭션에서 발생합니다. 따라서 단일 트랜잭션이 두 삽입을 모두 수행하는지 확인합니다. 행운을 빕니다. – lorinpa죄송합니다. 오타. 내 의견을 읽어야한다 : "그래서 귀하의 전제는 전적으로 하나의 트랜잭션에서 발생하는 2 개의 삽입에 의존합니다.". 전제는 데이터베이스가 아닌 트랜잭션 관리자가 초기 삽입을 롤백합니다. – lorinpa