Как определить транзакции отката, которые вызываются внутри цикла с помощью Spring AOP?

Ранее я создал метод updateRecord() в классе com.TestTransaction. Метод updateRecord() имеет цикл for для вставки значений в базу данных. Если внутри цикла возникает какое-либо исключение, все вставленные значения будут откатываться. Это отлично работает, и код выглядит следующим образом:

Внутри файла класса java

public class com.TestTransaction{
   ...
   //this is a big transaction
   public void updateRecord(){
      for(int i=0;i<5;i++){
         //insert value to database...
         //...if a runtime exception thrown here,
         //updateA() method will rollback as a whole transaction,
         //so all updates which were done inside the loop will rollback
      }
   }
   ...
}

Внутри файла config.xml (файл конфигурации Spring)

<bean id="masterTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
   <property name="dataSource" ref="masterDataSource" />
   <property name="nestedTransactionAllowed" value="true" />
</bean>
...
<aop:config proxy-target-class="true">
   <aop:pointcut id="testTransactionTX" expression="execution(* com.TestTransaction.*(..))"/>
   <aop:advisor pointcut-ref="testTransactionTX" advice-ref="defaultTxAdvice"/>
</aop:config>
...
<tx:advice id="defaultTxAdvice" transaction-manager="masterTxManager">
   <tx:attributes>
      <tx:method name="update*" propagation="REQUIRED"/>
   </tx:attributes>
</tx:advice>

Затем я решил сделать код внутри цикла метода updateRecord() отдельным методом doUpdateRecord(). Таким образом, когда doUpdateRecord() выдает исключение RuntimeException, он откатывает только этот doUpdateRecord(), и все предыдущие обновления будут зафиксированы. Но похоже, что откатиться не удается.

Код, как показано ниже:

public class com.TestTransaction{
   ...
   //this is no longer a big transaction
   public void updateRecord(){
      for(int i=0;i<5;i++){
         //every doUpdateRecord() call will start a new transaction
         doUpdateRecord();
      }
   }

   //this is a transaction
   public void doUpdateRecord(){
         //insert value to database...
         //...if a runtime exception thrown here,
         //it only rollback this method
   }
}

Конфигурационный файл весны:

<bean id="masterTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
       <property name="dataSource" ref="masterDataSource" />
       <property name="nestedTransactionAllowed" value="true" />
    </bean>
    ...
    <aop:config proxy-target-class="true">
       <aop:pointcut id="testTransactionTX" expression="execution(* com.TestTransaction.doUpdateRecord(..))"/>
       <aop:advisor pointcut-ref="testTransactionTX" advice-ref="defaultTxAdvice"/>
    </aop:config>
    ...
    <tx:advice id="defaultTxAdvice" transaction-manager="masterTxManager">
       <tx:attributes>
          <tx:method name="*" propagation="REQUIRES_NEW"/>
       </tx:attributes>
    </tx:advice>

Может ли кто-нибудь дать какие-либо идеи о том, что происходит? Можно ли откатить одно обновление при вызове метода (транзакции) внутри цикла?


person Redd Qian    schedule 28.11.2011    source источник


Ответы (2)


Возможно, я столкнулся с этим в этом вопросе. Вызовы внутри одного и того же класса не проходят через прокси, и ваша логика pointcut игнорируется.

person MaDa    schedule 28.11.2011
comment
Спасибо, МаДа, точно такая же проблема. Я создал новый класс, и на данный момент проблема решена. Вот новая проблема, мне нужно реструктурировать beans, я думаю. На данный момент это выглядит таким беспорядком. - person Redd Qian; 28.11.2011
comment
@ReddQian Пожалуйста, очень жаль, что вы приняли ответ под копирку, который пришел позже. - person MaDa; 28.11.2011

Поскольку Spring поддерживает только аспекты при вызове метода, который знает о вас, Spring не сможет перехватить этот doUpdateRecord, поскольку он не был вызван для управляемого Spring экземпляра bean-компонента. Чтобы аспект работал, необходимо вызвать doUpdateRecord для экземпляра bean-компонента Spring, потому что только тогда Spring может перехватить вызов метода и вставить транзакционный материал.

Либо вы вводите экземпляр Spring bean-компонента TestTransaction в саму тестовую транзакцию и ссылаетесь на него при вызове doUpdateRecord (не уверен, что это сработает), либо вы перемещаете код doUpdateRecord в другой bean-компонент и вызываете этот bean-компонент.

person Michael Wiles    schedule 28.11.2011
comment
последний, кажется, работает на данный момент, попробуем другой позже, спасибо, Майкл! - person Redd Qian; 28.11.2011