Announcement Announcement Module
Collapse
No announcement yet.
Spring looks for rollback rules twice, the second one doesn't find my rule Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring looks for rollback rules twice, the second one doesn't find my rule

    Hi everyone,

    I'm using Spring declarative transactions. I want Spring to rollback transaction on checked exception. Should be simple.

    applicationConext.xml:
    Code:
    	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<property name="persistenceUnitName" value="x"/>
    		<property name="dataSource" ref="dataSource"/>
    	</bean>
    
    	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    		<property name="entityManagerFactory" ref="entityManagerFactory"/>
    	</bean>
    
    	<tx:annotation-driven transaction-manager="transactionManager" order="100"/>
    
    	<tx:advice id="defaultTxAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    			<tx:method name="*" rollback-for="Throwable"/>
    		</tx:attributes>
    	</tx:advice>
    
    	<aop:config>
    		<aop:pointcut id="defaultServiceOperation" expression="execution(* (@org.springframework.stereotype.Service *).*(..))"/>
    		<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
    	</aop:config>
    
    	<aop:aspectj-autoproxy/>
    My method is annotated by @Transactional, everything works OK if this method doesn't throw an exception. If it does I can see in my logs, that Spring looks for rollback rules twice. The first time it finds my rule OK and marks transaction as rollbackOnly, the second one it finds no rules, falls back to default and tries to commit. But transaction is marked as rollbackOnly, so commit fails, but my web tier gets Spring exception instead of mine. Here's log from my test:

    Code:
    INFO [pl.x.y.psi.service.impl.TimePeriodServiceImpl:468] - Unable to block time period, conflicts exists: Time Period 1
    DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor:387] - Completing transaction for [pl.x.y.psi.service.api.TimePeriodService.blockTimePeriod] after exception: pl.x.y.psi.exception.ConflictedSalesPlanException: Unable to block time period, conflicts exists: Time Period 1
    DEBUG [org.springframework.transaction.interceptor.RuleBasedTransactionAttribute:130] - Applying rules to determine whether transaction should rollback on pl.x.y.psi.exception.ConflictedSalesPlanException: Unable to block time period, conflicts exists: Time Period 1
    DEBUG [org.springframework.transaction.interceptor.RuleBasedTransactionAttribute:147] - Winning rollback rule is: RollbackRuleAttribute with pattern [Throwable]
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:850] - Participating transaction failed - marking existing transaction as rollback-only
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:513] - Setting JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@23f2bc83] rollback-only
    DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor:387] - Completing transaction for [pl.x.y.psi.service.api.TimePeriodService.blockTimePeriod] after exception: pl.x.y.psi.exception.ConflictedSalesPlanException: Unable to block time period, conflicts exists: Time Period 1
    DEBUG [org.springframework.transaction.interceptor.RuleBasedTransactionAttribute:130] - Applying rules to determine whether transaction should rollback on pl.x.y.psi.exception.ConflictedSalesPlanException: Unable to block time period, conflicts exists: Time Period 1
    DEBUG [org.springframework.transaction.interceptor.RuleBasedTransactionAttribute:147] - Winning rollback rule is: null
    DEBUG [org.springframework.transaction.interceptor.RuleBasedTransactionAttribute:152] - No relevant rollback rule found: applying default rules
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:925] - Triggering beforeCommit synchronization
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:938] - Triggering beforeCompletion synchronization
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:752] - Initiating transaction commit
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:462] - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@23f2bc83]
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:967] - Triggering afterCompletion synchronization
    DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager:316] - Clearing transaction synchronization
    DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager:229] - Removed value [org.springframework.orm.jpa.EntityManagerHolder@3e7c609] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@657a7adf] from thread [main]
    DEBUG [org.springframework.orm.jpa.JpaTransactionManager:548] - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@23f2bc83] after transaction
    DEBUG [org.springframework.orm.jpa.EntityManagerFactoryUtils:328] - Closing JPA EntityManager
    ERROR [org.springframework.transaction.interceptor.TransactionInterceptor:415] - Application exception overridden by commit exception
    pl.x.y.psi.exception.ConflictedSalesPlanException: Unable to block time period, conflicts exists: Time Period 1
    I'm using Spring 3.0.3.GA with JPA2 provided by Hibernate 3.5.3-Final. What am I missing?

    Best regards

    Jacek Bilski

  • #2
    Hi,

    Not a solution yet, but a few notes.

    1. I checked it with Spring 3.0.5.RELEASE, but got the same result. With 3.1.0.M2 nothing changed.

    2. I disabled my other aspects to be sure they do not interfere with transactions, but it doesn't matter.

    3. I changed pointcut for rollback rule to:
    Code:
    <aop:pointcut id="defaultServiceOperation" expression="@annotation(org.springframework.transaction.annotation.Transactional)"/>
    Not every service method is transactional.

    4. I use AspectJ version 1.6.8, but also checked with 1.6.10, no change.

    Now, I got it working, but only when I annotated my method with:
    Code:
    @Transactional(rollbackFor = ConflictedSalesPlanException.class)
    It's not what I want, since we have many transactional methods, and I don't want to annotate every one of them, I'm looking for general solution.

    I've yet to dig deeper, but for now it seems, that Spring sometimes finds and sometimes doesn't find it. I was debugging it and found out that RuleBasedTransactionAttribute is created about 15-20 times, but only once any rules are set. I'll try to see why.

    Any clues?

    Best regards

    Jacek Bilski

    Comment


    • #3
      Solution

      Hi,

      OK, stupid me. It's simple, really. What I was trying to do, was to add something to configuration od transactions, instead I created completely new set of transactional methods. You can't mix @Transactional with <tx:advice...>.

      The best solution to configuring multiple @Transactional with similar properties, is to create own annotation:
      Code:
      @Target({ElementType.METHOD, ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Transactional(rollbackFor = Exception.class)
      public @interface MyTx {
      }
      I'm going to try that.

      Anyway, thanks for help, my understanding of transactions in Spring is far greater than before

      Best regards

      Jacek Bilski

      Comment


      • #4
        Thanks to God I refreshed this thread and you already realized about

        You can't mix @Transactional with <tx:advice...>.
        I suggest you read more about Transactions on the Spring Documentation

        The best solution to configuring multiple @Transactional with similar properties
        Could you expand the idea here? I cant understand this situation (bold part), some code would be useful to get a better idea

        Comment


        • #5
          Originally posted by dr_pompeii View Post
          I suggest you read more about Transactions on the Spring Documentation
          I actually did, but only parts I thought I needed. Apparently that's why I missed that part, where you can't mix annotations with XML. On the other hand in documentation to version 3.0.3, I didn't find any exclamation not to do so...

          Originally posted by dr_pompeii View Post
          Could you expand the idea here? I cant understand this situation (bold part), some code would be useful to get a better idea
          As I said the first time, I want all my transactional methods, by default, to rollback when any Exception is thrown from within them. But I don't want to annotate every method with
          Code:
          @Transactional(rollbackFor = Exception.class)
          possibly forgetting setting parameters. Instead I can define my own annotation
          Code:
          @Target({ElementType.METHOD, ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Transactional(rollbackFor = Exception.class)
          public @interface MyTx {
          }
          and then annotate all my transactional methods with my annotation.

          It's not perfect, I would still want to annotate my methods somehow, and be able to configure transactions mechanism elsewhere. But having written that out, I'm not so sure if it's such great idea to do one thing in two places. I have my homework to do in that matter.

          Best regards

          Jacek Bilski

          Comment


          • #6
            Hello

            As I said the first time, I want all my transactional methods, by default, to rollback when any Exception is thrown from within them. But I don't want to annotate every method with
            You can use this line @Transactional(rollbackFor = Exception.class) just in your class scope, I mean

            Code:
            @Transactional(rollbackFor = Exception.class)
            public class SomeBOImpl implements SomeBOService{
            ....
            }
            And all your methods can be now Transactional without declare each @Transactional annotation for each method, even more, you can override explicitly some property or attribute for the @Transactional annotation for some method

            Again read the documentation about this

            Comment


            • #7
              Hi,

              Originally posted by dr_pompeii View Post
              You can use this line @Transactional(rollbackFor = Exception.class) just in your class scope
              I know, but not every method in my services are transactional.

              Jacek Bilski

              Comment


              • #8
                OK


                I know, but not every method in my services are transactional.
                Tell me. why you could want have a business class with some methods without transactional control?

                Comment


                • #9
                  Originally posted by dr_pompeii View Post
                  Tell me. why you could want have a business class with some methods without transactional control?
                  We have some situations in which we iterate over many objects and do operation on every one. To avoid deadlocks because of lenghty transactions we simply have two methods, one that does it all (nontransactional), ane second one, which processes one entity (transactional). Of course, we know how to call method from the same bean to be transactional (get bean itself from context and call this method on bean). The outer method takes care of situations, when inner method throws an exception.

                  I know it may not be perfect, maybe it should be done in the web tier, but it works.

                  Even more, if I would annotate my whole bean as a transactional, then I would need annotating my "Read-only" methods nevertheless. We're still working on our best approach.

                  Jacek Bilski

                  Comment

                  Working...
                  X