2011-03-08 62 views
15

当使用嵌套的Spring事务时,我发现了一些奇怪的行为:在同一个类中,如果注释为@Transactional的方法调用另一个也注释为@Transactional的方法,则不使用第二个注释。Spring上的嵌套事务

让我们考虑下面的类:

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我希望新的交易为每次调用创建,因为两个插入都使用相同的顶级事务,所以不会发生这种情况。

第三操作失败,以及没有交易可言创建:

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 

@Configuration豆弹簧确保调用对同一类中proxified这显然是这里没有发生的方法。有没有办法改变这种行为?

回答

8

此行为是对AOP使用proxy模式时的Spring记录的行为。可以通过切换到在编译时或运行时执行代码检测的aspectj模式来更改它。

6

这不是@Transactional的特别问题。这是由于您的配置<tx:annotation-driven/>

Spring使用两种不同的AOP机制:JDK动态代理或CGLIB。 JDK动态代理是默认的,它通过使用运行时间的接口来工作。 CGLIB通过在编译时间生成子类来工作。如果你指定<tx:annotation-driven proxy-target-class="true"/>,Spring将使用CGLIB,并且你的第二个@Transactional将会触发。

您可以阅读更多关于此主题here