Announcement Announcement Module
Collapse
No announcement yet.
Nested Transaction does not rollback for Throwable Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Nested Transaction does not rollback for Throwable

    Hi all

    I have an outer bean IAstuteCallback which calls an inner bean IDataHandler (in a loop and I want each loop to commit or rollback).

    Code:
    IAstuteCallback.saveData() {  --> PROPAGATION_REQUIRED,-MyException1,-MyException2
        IDataHandler.handleData() --> PROPAGATION_REQUIRES_NEW,-DataHandlerException,-Throwable
    }
    If the inner bean IDataHandler throws a DataHandlerException then the transaction is rolled back and the data is not persisted to the database.

    But if the inner bean throws a Throwable (eg: org.springframework.dao.InvalidDataAccessApiUsageE xception - incorrect Hiberante usage) then the transaction is not rolled back and the data is persisted. Which is counter to what should happen so if someone could point out what I am doing wrong please?

    The outer bean:
    Code:
    <!-- The Astute Adapter transaction attributes bean -->
    <bean id="astute.adapter.transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
    	<property name="properties">
    		<props>
    			<prop key="*">
    			PROPAGATION_REQUIRED,-MyException1,-MyException2
    			</prop>
    		</props>
    	</property>
    </bean>
    <!-- The Astute Adapter bean which has transactions for its method  -->
    <bean id="agreement.astute.support.adapter" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          <property name="proxyInterfaces">
                <list>
                	<value>IAstuteAdapter</value>
                    <value>IAstuteCallback</value>
                </list>
          </property>
          <property name="target">
                   <ref bean="agreement.astute.support.adapter.impl"/>
          </property>
          <property name="transactionManager">
                <ref bean="support.transaction.manager"/>
          </property>
          <property name="transactionAttributeSource">
    			<ref bean="astute.adapter.transactionAttributeSource"/>
          </property>
    </bean>
    The inner bean:
    Code:
    <!-- The transaction attributes bean -->
    <bean id="astute.transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
    	<property name="properties">
    		<props>
    			<prop key="handleData">
    			PROPAGATION_REQUIRES_NEW,-DataHandlerException,-Throwable
    			</prop>
    		</props>
    	</property>
    </bean>
    
    <!-- The IAstuteDataHandler bean which has transactions for its method  -->
    <bean id="agreement.astute.data.handler" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          <property name="proxyInterfaces">
                <list>
                   <value>IAstuteDataHandler</value>
                </list>
          </property>
          <property name="target">
                   <ref bean="agreement.astute.data.handler.impl"/>
          </property>
          <property name="transactionManager">
                <ref bean="support.transaction.manager"/>
          </property>
          <property name="transactionAttributeSource">
    			<ref bean="astute.transactionAttributeSource"/>
          </property>
    </bean>
    I tried removing the outer transaction from the IAstuteCallback.saveData() method but then I get:
    Code:
    org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope
    .

    I am using Spring 2.0.5, Hibernate 3.1.1 and the code is being called from a JUnitTest.

    Any suggestion would be appreciated!
    Thanks Aisling

  • #2
    Can you post the bean definitions of the target beans also? Maybe on wiring you do not refer to the transactional proxy but to the plain target. That would also explain the missing transactional scope on removing the outer transaction.

    Comment


    • #3
      Thanks for your reply.

      The JUnitTest performs a lookup for the IAstuteCallback bean (outer bean):
      Code:
      protected IAstuteCallback astuteCallback = (IAstuteCallback) SupportSpringLoader.getBean(
                                                  SupportConstants.ASTUTE_ADAPTER);
      where
      Code:
      public static final String ASTUTE_ADAPTER = "agreement.astute.support.adapter";
      The outer bean definition is agreement.astute.support.adapter defined in my original post and its target (generated by xdoclet) is:
      Code:
      <bean
            id="agreement.astute.support.adapter.impl"
            class="za.co.sanlam.harold.support.agreement.astute.AstuteAdapter"
        >
          <property name="personDao">
            <ref bean="dao.person"/>
          </property>
          <property name="astuteDAO">
            <ref bean="dao.astute"/>
          </property>
          <property name="astuteDataHandler">
            <ref bean="agreement.astute.data.handler"/>
          </property>
          <property name="astuteProvidersHandler">
            <ref bean="agreement.astute.support.astuteprovider.handler"/>
          </property>
          <property name="astuteService">
            <ref bean="agreement.astute.service"/>
          </property>
          <property name="validatorHelper">
            <ref bean="support.validator.helper"/>
          </property>
          <property name="messageSource">
            <ref bean="support.message.source"/>
          </property>
        </bean>
      The agreement.astute.data.handler is the inner bean (defined in my original post) which refers to this target bean:
      Code:
      <bean
            id="agreement.astute.data.handler.impl"
            class="za.co.sanlam.harold.support.agreement.astute.handler.AstuteDataHandler">
          <property name="astuteDataMapper">
            <ref bean="agreement.common.provider.data.extractor"/>
          </property>
          <property name="astuteDAO">
            <ref bean="dao.astute"/>
          </property>
          <property name="domainTypeFacade">
            <ref bean="facade.domaintypes"/>
          </property>
          <property name="productAgreementDao">
            <ref bean="dao.productagreement"/>
          </property>
        </bean>
      I hope this is what you are looking for?
      Thanks Aisling

      Comment


      • #4
        I have not used transaction nesting like this myself. However, it seems ok to me so far. I suggest turning on debug logging and see if there might be something of interest.
        First there should be log entries stating that the relevant methods on both types are made transactional. Then the part during the actual invocation would be interesting as well.

        Regards,
        Andreas

        Comment


        • #5
          Yes - I am not too sure if I actually need the outer transaction but I want the inner transaction to commit or rollback after each loop of saving data.

          Here is my log output:
          Code:
          INFO  Dialect  - Using dialect: org.hibernate.dialect.DB2Dialect
          INFO  TransactionFactoryFactory  - Using default transaction strategy (direct JDBC transactions)
          INFO  TransactionManagerLookupFactory  - No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
          INFO  SettingsFactory  - Automatic flush during beforeCompletion(): disabled
          INFO  SettingsFactory  - Automatic session close at end of transaction: disabled
          INFO  SettingsFactory  - Scrollable result sets: enabled
          INFO  SettingsFactory  - JDBC3 getGeneratedKeys(): disabled
          INFO  SettingsFactory  - Connection release mode: on_close
          INFO  SettingsFactory  - Default schema: HAROLD_INGT
          INFO  SettingsFactory  - Default batch fetch size: 1
          INFO  SettingsFactory  - Generate SQL with comments: disabled
          INFO  SettingsFactory  - Order SQL updates by primary key: disabled
          INFO  SettingsFactory  - Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
          INFO  ASTQueryTranslatorFactory  - Using ASTQueryTranslatorFactory
          INFO  SettingsFactory  - Query language substitutions: {}
          INFO  SettingsFactory  - Second-level cache: enabled
          INFO  SettingsFactory  - Query cache: disabled
          INFO  SettingsFactory  - Cache provider: org.hibernate.cache.EhCacheProvider
          INFO  SettingsFactory  - Optimize cache for minimal puts: disabled
          INFO  SettingsFactory  - Structured second-level cache entries: disabled
          INFO  SettingsFactory  - Statistics: disabled
          INFO  SettingsFactory  - Deleted entity synthetic identifier rollback: disabled
          INFO  SettingsFactory  - Default entity-mode: pojo
          INFO  SessionFactoryImpl  - building session factory
          INFO  SessionFactoryObjectFactory  - Not binding factory to JNDI, no JNDI name configured
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method [handleData] with attribute [PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-za.co.sanlam.harold.support.agreement.astute.handler.exception.DataHandlerException,-Throwable]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
          DEBUG NameMatchTransactionAttributeSource  - Adding transactional method[*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-za.co.sanlam.harold.integration.agreement.astute.exception.AstuteIntegrationException,-za.co.sanlam.harold.support.common.exception.HaroldSupportException]
          ............
          Code:
          DEBUG TransactionInterceptor  - Completing transaction for [za.co.sanlam.harold.support.agreement.astute.handler.IAstuteDataHandler.handleData]
          ERROR AbstractFlushingEventListener  - Could not synchronize database state with session
          org.hibernate.TransientObjectException: za.co.sanlam.harold.model.payment.PaymentPart
          	at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:216)
          ....
          DEBUG TransactionSynchronizationManager  - Removed value [org.springframework.orm.hibernate3.SessionHolder@f471e] for key [org.hibernate.impl.SessionFactoryImpl@18a9e64] from thread [main]
          DEBUG TransactionSynchronizationManager  - Clearing transaction synchronization
          DEBUG TransactionSynchronizationManager  - Removed value [org.springframework.orm.hibernate3.SessionHolder@1cec59b] for key [org.hibernate.impl.SessionFactoryImpl@1be496b] from thread [main]
          DEBUG TransactionSynchronizationManager  - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@121cbdb] for key [org.apache.commons.dbcp.BasicDataSource@a620f5] from thread [main]
          DEBUG TransactionSynchronizationManager  - Bound value [org.springframework.orm.hibernate3.SessionHolder@115272a] for key [org.hibernate.impl.SessionFactoryImpl@1be496b] to thread [main]
          DEBUG TransactionSynchronizationManager  - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@15e10ab] for key [org.apache.commons.dbcp.BasicDataSource@a620f5] to thread [main]
          DEBUG TransactionSynchronizationManager  - Initializing transaction synchronization
          DEBUG TransactionSynchronizationManager  - Bound value [org.springframework.orm.hibernate3.SessionHolder@c8a027] for key [org.hibernate.impl.SessionFactoryImpl@18a9e64] to thread [main]
          DEBUG TransactionInterceptor  - Completing transaction for [za.co.sanlam.harold.integration.agreement.astute.callback.IAstuteCallback.saveData] after exception: org.springframework.dao.InvalidDataAccessApiUsageException: za.co.sanlam.harold.model.payment.PaymentPart; nested exception is org.hibernate.TransientObjectException: za.co.sanlam.harold.model.payment.PaymentPart
          DEBUG RuleBasedTransactionAttribute  - Applying rules to determine whether transaction should rollback on org.springframework.dao.InvalidDataAccessApiUsageException: za.co.sanlam.harold.model.payment.PaymentPart; nested exception is org.hibernate.TransientObjectException: za.co.sanlam.harold.model.payment.PaymentPart
          DEBUG RuleBasedTransactionAttribute  - Winning rollback rule is: null
          DEBUG RuleBasedTransactionAttribute  - No relevant rollback rule found: applying superclass default
          DEBUG TransactionSynchronizationManager  - Removed value [org.springframework.orm.hibernate3.SessionHolder@c8a027] for key [org.hibernate.impl.SessionFactoryImpl@18a9e64] from thread [main]
          DEBUG TransactionSynchronizationManager  - Clearing transaction synchronization
          I can see that the outer bean's transaction is the one that is checking whether to commit or rollback.
          (Ideally I would like the check on the inner transaction but I suppose the outer one is fine too as long as invalid data is not committed.)

          It seems that if it applies the superclass default (which superclass?) then it should roll back for org.springframework.dao.InvalidDataAccessApiUsageE xception as it extends RuntimeException. But the data is committed to the database.

          Thanks

          Comment

          Working...
          X