Results 1 to 9 of 9

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

  1. #1
    Join Date
    Dec 2008
    Posts
    26

    Default 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. #2
    Join Date
    Dec 2008
    Posts
    26

    Default

    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

  3. #3
    Join Date
    Dec 2008
    Posts
    26

    Default 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

  4. #4
    Join Date
    Aug 2006
    Location
    Arequipa-Peru / South America
    Posts
    2,796

    Default

    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
    - Manuel Jordan

    Kill Your Pride, Share Your Knowledge With All
    The Fear Of The LORD Is The Beginning Of Knowledge, But Fools Despise Wisdom And Discipline. Proverbs 1:7

    Blog


    Technical Reviewer of Apress

    • Pro SpringSource dm Server
    • Spring Enterprise Recipes: A Problem-Solution Approach
    • Spring Recipes: A Problem-Solution Approach, 2nd Edition
    • Pro Spring Integration
    • Pro Spring Batch
    • Pro Spring 3
    • Pro Spring MVC: With Web Flow
    • Pro Spring Security

  5. #5
    Join Date
    Dec 2008
    Posts
    26

    Default

    Quote 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...

    Quote 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

  6. #6
    Join Date
    Aug 2006
    Location
    Arequipa-Peru / South America
    Posts
    2,796

    Default

    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
    - Manuel Jordan

    Kill Your Pride, Share Your Knowledge With All
    The Fear Of The LORD Is The Beginning Of Knowledge, But Fools Despise Wisdom And Discipline. Proverbs 1:7

    Blog


    Technical Reviewer of Apress

    • Pro SpringSource dm Server
    • Spring Enterprise Recipes: A Problem-Solution Approach
    • Spring Recipes: A Problem-Solution Approach, 2nd Edition
    • Pro Spring Integration
    • Pro Spring Batch
    • Pro Spring 3
    • Pro Spring MVC: With Web Flow
    • Pro Spring Security

  7. #7
    Join Date
    Dec 2008
    Posts
    26

    Default

    Hi,

    Quote 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

  8. #8
    Join Date
    Aug 2006
    Location
    Arequipa-Peru / South America
    Posts
    2,796

    Default

    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?
    - Manuel Jordan

    Kill Your Pride, Share Your Knowledge With All
    The Fear Of The LORD Is The Beginning Of Knowledge, But Fools Despise Wisdom And Discipline. Proverbs 1:7

    Blog


    Technical Reviewer of Apress

    • Pro SpringSource dm Server
    • Spring Enterprise Recipes: A Problem-Solution Approach
    • Spring Recipes: A Problem-Solution Approach, 2nd Edition
    • Pro Spring Integration
    • Pro Spring Batch
    • Pro Spring 3
    • Pro Spring MVC: With Web Flow
    • Pro Spring Security

  9. #9
    Join Date
    Dec 2008
    Posts
    26

    Default

    Quote 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

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •