복붙노트

[SPRING] Spring / JTA / JPA 유닛 테스트 : 롤백이 작동하지 않습니다.

SPRING

Spring / JTA / JPA 유닛 테스트 : 롤백이 작동하지 않습니다.

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;
    }
    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;
    }

}

내 단위 테스트 :

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);
    }

}

및 내 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>

테스트를 실행하면 test2가 실패합니다. 왜냐하면 처음 엔 하나만 예상했기 때문에 2 개의 엔티티를 찾을 수 있기 때문입니다 (첫 번째는 롤백되어야했기 때문에 ...)

나는 다른 구성을 많이 시도했는데이 중 가장 포괄적 인 것으로 보인다 ... 나는 다른 아이디어가 없다. 너?

해결법

  1. ==============================

    1.JOTM과 Hibernate를 통합하려고 할 때, 결국 ConnectionProvider 구현을 코딩해야했다. 다음은 현재 보이는 모습입니다. http://pastebin.com/f78c66e9c

    JOTM과 Hibernate를 통합하려고 할 때, 결국 ConnectionProvider 구현을 코딩해야했다. 다음은 현재 보이는 모습입니다. http://pastebin.com/f78c66e9c

    그런 다음 당신은 하이버 네이트 프로퍼티와 트랜잭션에서 연결 privider가 마술처럼 작동하기 시작할 때 구현을 지정한다.

    것은 기본 연결 공급자가 데이터 소스에서 getConnection ()을 호출한다는 것입니다. 자신 만의 구현에서는 getXAConnection (). getConnection ()을 호출합니다. 이것은 차이를 만든다.

  2. ==============================

    2.JOTM 대신 Bitronix를 사용하여 작동하도록했습니다. Bitronix는 비 XA 데이터베이스가 JTA 트랜잭션에 참여할 수있게하는 LrcXADataSource를 제공합니다.

    JOTM 대신 Bitronix를 사용하여 작동하도록했습니다. Bitronix는 비 XA 데이터베이스가 JTA 트랜잭션에 참여할 수있게하는 LrcXADataSource를 제공합니다.

    문제는 H2가 XA 규격이 아니며 enhandra StandardXADataSource가 마술처럼 만들지 않는다는 것입니다 (HSQLDB를 사용하여 끝났지 만 문제와 관련이 없습니다).

    작동하는 나의 봄 문맥은 여기있다 :

    <?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>
    
  3. ==============================

    3.편집 : (죄송합니다,이 단락을 썼을 때 나는 깨어있는 것 같았습니다. 물론 맞았습니다. 모든 것이 기본적으로 롤백되어야합니다.)

    편집 : (죄송합니다,이 단락을 썼을 때 나는 깨어있는 것 같았습니다. 물론 맞았습니다. 모든 것이 기본적으로 롤백되어야합니다.)

    트랜잭션 관리자가 실제로 수행중인 작업을 확인할 수 있습니다 (예 : 디버그 출력을 사용 가능하게 설정).

    log4j 가정 :

    log4j.logger.org.springframework.transaction=DEBUG
    

    트랜잭션 관리자는 생성 된 트랜잭션과 조인 된 트랜잭션, 커밋 및 롤백에 대한 매우 훌륭한 로그 출력을 제공합니다. 그게 당신이 당신의 셋업으로 일하지 않는 것을 알아내는 데 도움이 될 것입니다.

  4. ==============================

    4.스프링 문서에서 언급 한 @Transactional 어노테이션 바로 뒤에 @Rollback 어노테이션 (org.springframework.test.annotation에서)을 추가하십시오.

    스프링 문서에서 언급 한 @Transactional 어노테이션 바로 뒤에 @Rollback 어노테이션 (org.springframework.test.annotation에서)을 추가하십시오.

    @Rollback is a test annotation that is used to indicate whether a test-
    managed transaction should be rolled back after the test method has 
    completed. 
    Consult the class-level Javadoc for 
    org.springframework.test.context.transaction.TransactionalTest-
    ExecutionListener for an explanation of test-managed transactions. 
    
    When declared as a class-level annotation, @Rollback defines the default 
    rollback semantics for all test methods within the test class hierarchy. When 
    declared as a method-level annotation, @Rollback defines rollback semantics 
    for the specific test method, potentially overriding class-level default 
    commit or rollback semantics. 
    
    As of Spring Framework 4.2, @Commit can be used as direct replacement for 
    @Rollback(false). 
    
    Warning: Declaring @Commit and @Rollback on the same test method or on the 
    same test class is unsupported and may lead to unpredictable results. 
    
    This annotation may be used as a meta-annotation to create custom composed 
    annotations. Consult the source code for @Commit for a concrete example.
    
  5. from https://stackoverflow.com/questions/1519968/spring-jta-jpa-unit-test-rollback-not-working by cc-by-sa and MIT license