2011-03-08 4 views
15

중첩 된 스프링 트랜잭션을 사용할 때 이상한 동작이 발견되었습니다. 같은 클래스에서 @Transactional으로 주석 처리 된 메소드는 @Transactional으로 주석 처리 된 다른 메소드를 호출 할 때 두 번째 주석이 사용되지 않습니다.스프링에 중첩 된 트랜잭션

의는 다음 클래스를 살펴 보자 :

public class Main { 
    public static void main(String[] args) { 
     ApplicationContext context = new AnnotationConfigApplicationContext(Config.class); 
     final Main main = context.getBean(Main.class); 
     // First Op 
     System.out.println("Single insert: " + main.singleInsert()); 
     // Second Op 
     main.batchInsert(); 
     // Third Op 
     main.noTransBatchInsert(); 
    } 

    @PersistenceContext 
    private EntityManager pm; 

    @Transactional(propagation=Propagation.REQUIRED) 
    public void batchInsert() { 
     System.out.println("batchInsert"); 
     System.out.println("First insert: " + singleInsert()); 
     System.out.println("Second insert: " + singleInsert()); 
    } 

    public void noTransBatchInsert() { 
     System.out.println("noTransBatchInsert"); 
     System.out.println("First insert: " + singleInsert()); 
     System.out.println("Second insert: " + singleInsert()); 
    } 

    @Transactional(propagation=Propagation.REQUIRES_NEW) 
    public int singleInsert() { 
     System.out.println("singleInsert"); 
     Pojo p = new Pojo(); 
     pm.persist(p); 
     return p.getId(); 
    } 
} 

엔티티의 경우 다음 클래스 :

@Entity 
public class Pojo { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private int id; 

    @Override 
    public String toString() { 
     return "Pojo: " + id; 
    } 

    public int getId() { 
     return id; 
    } 
} 

및 문자열 부분 applicationContext.xml :

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" 
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" 
    xsi:schemaLocation=" 
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-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/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 

    <tx:annotation-driven /> 

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> 
     <property name="persistenceUnitName" value="MyPersistenceUnit" /> 
    </bean> 

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
     <property name="entityManagerFactory" ref="entityManagerFactory" /> 
    </bean> 
</beans> 

및 구성 클래스 (나는 이것을 applicationContext.xml에 병합 할 수 있었다). 완성도를 들어

@Configuration 
@ImportResource("/META-INF/applicationContext.xml") 
public class Config { 

    @Bean 
    public Main main() { 
     return new Main(); 
    } 
} 

persistence.xml 파일 :

그 새로운 트랜잭션에 예상대로
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> 

    <persistence-unit name="MyPersistenceUnit" transaction-type="RESOURCE_LOCAL"> 
     <provider>org.hibernate.ejb.HibernatePersistence</provider> 

     <properties> 
      <property name="hibernate.hbm2ddl.auto" value="create" /> 
      <property name="hibernate.show_sql" value="true" /> 
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> 
      <property name="hibernate.connection.driver_class" value="org.h2.Driver" /> 
      <property name="hibernate.connection.url" value="jdbc:h2:mem:TestDSJPA2;DB_CLOSE_DELAY=-1;LOCK_MODE=0" /> 
      <!--<property name="hibernate.connection.url" value="jdbc:h2:mem:TestDSJPA2;DB_CLOSE_DELAY=-1;LOCK_MODE=0" />--> 
      <property name="hibernate.connection.username" value="sa" /> 
      <property name="hibernate.connection.password" value="" /> 
      <property name="hibernate.connection.autocommit" value="false"/> 

      <property name="hibernate.c3p0.min_size" value="5" /> 
      <property name="hibernate.c3p0.max_size" value="20" /> 
      <property name="hibernate.c3p0.timeout" value="300" /> 
      <property name="hibernate.c3p0.max_statements" value="50" /> 
      <property name="hibernate.c3p0.idle_test_period" value="3000" /> 
     </properties> 

    </persistence-unit> 
</persistence> 

그래서 메인 클래스의 첫 번째 작업이 수행됩니다. (일부 디버그 메시지 포함)의 출력은 다음과 같습니다

DEBUG o.h.transaction.JDBCTransaction - begin 
singleInsert 
DEBUG o.h.transaction.JDBCTransaction - commit 
Single insert: 1 

두 번째 작업은 다음과 같은 출력을 제공합니다 :

batchInsert 
singleInsert 
DEBUG o.h.transaction.JDBCTransaction - begin 
First insert: 2 
singleInsert 
Second insert: 3 
DEBUG 

@Transactional(propagation=Propagation.REQUIRES_NEW)와 singleInsert 주석에 나는 새로운 거래를 기대 때문에 내가 기대했던 것이 아니다 두 가지 삽입에 동일한 최상위 트랜잭션이 사용되기 때문에 발생하는 모든 호출에 대해 생성됩니다.

세 번째 작업은 물론 어떤 트랜잭션이 전혀 생성되지 않습니다으로 실패 : 여기 안될 분명히 proxified되어 같은 클래스의 메소드에 호출 @Configuration 콩 봄 보장하지만에서

noTransBatchInsert 
singleInsert 
DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress 
First insert: 0 
singleInsert 
DEBUG o.h.e.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress 
Second insert: 0 

. 이 동작을 변경하는 방법이 있습니까?

답변

8

이 동작은 AOP 용 proxy 모드를 사용할 때 Spring의 문서화 된 동작입니다. 컴파일시 또는 런타임에 코드 계측을 수행하는 aspectj 모드로 전환하여 변경할 수 있습니다.

6

@Transactional에서 특별히 문제는 아닙니다. <tx:annotation-driven/>의 구성 때문입니다.

스프링은 두 개의 다른 AOP 메커니즘 인 JDK 동적 프록시 또는 CGLIB를 사용합니다. JDK 동적 프록시가 기본값이며 -time에서 인터페이스를 사용하여 작동합니다. CGLIB는 에 하위 클래스를 생성하여 작동합니다. - 시간. <tx:annotation-driven proxy-target-class="true"/>을 지정하면 Spring에서 CGLIB를 사용하고 두 번째 @Transactional이 실행됩니다.

제목에 대한 자세한 내용은 here입니다.

관련 문제