Announcement Announcement Module
Collapse
No announcement yet.
Jms/Jdbc transaction interaction Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Jms/Jdbc transaction interaction

    I'm using the DefaultMessageListenerContainer, DataSourceTransactionManager, and declarative transactions using annotations. I'm not using XA. Chances are I'm just not familiar enough with all the interactions between the transactions. I've pored over the spring javadocs, documentation, and google, but am just not getting anywhere.

    In positive cases (nothing goes wrong) I want the jms transaction and jdbc transaction to commit at the end of my processing. I have duplicate message processing to handle the case where the JDBC transaction successfully commits, but the JMS one doesn't. This all works fine.

    The issue I have is in the negative cases. In some cases I want to rollback the JDBC transaction, but I still want to commit the JMS transaction. I don't see a way to break the JMS transaction off of the JDBC transaction in certain cases.

    As for setup:
    My SessionAwareMessageListener onMessage method is annotated with @Transactional to start the JDBC transaction and I have set the DefaultMessageListenerContainer sessionTransacted to true. Within the onMessage method I determine which transactions (JMS and/or JDBC) need to commit and which should rollback.

    Is there some simple class or setting that I'm not aware of that will let me do what I want? I could do some refactoring such that the @Transactional annotation is in a separate class called by my listener, but would prefer to keep it all together for now...

    Any suggestions are welcome.

    Thanks,
    Bob

  • #2
    Technically, if you are using JMS and JDBC in the same transaction, you need XA. Now, you can get away with using a single non XA participant (e.g. your database) with many transaction managers. They will use the 'last resource gambit' or 'last resource commit' for the database transaction...however, your JMS connection will still need to be XA.

    Comment


    • #3
      Originally posted by jacorob View Post
      In positive cases (nothing goes wrong) I want the jms transaction and jdbc transaction to commit at the end of my processing. I have duplicate message processing to handle the case where the JDBC transaction successfully commits, but the JMS one doesn't. This all works fine.

      The issue I have is in the negative cases. In some cases I want to rollback the JDBC transaction, but I still want to commit the JMS transaction. I don't see a way to break the JMS transaction off of the JDBC transaction in certain cases
      The only way you can commit the JMS message transaction when you rollback the JDBC work would be to have the JDBC work done in a nested transaction (e.g. 'requires new'). in case you don't want the rollback of the jdbc work to rollback the jms message you just trap any exceptions thrown by your jdbc component and bury it. In the case you DO want the rollback of the jdbc work to rollback the jms message you just need to let that exception bubble up to the JMS container and it will handle the rollback of the jms.

      The only case that you haven't described is the case where the particular jdbc work is successfuly but you DO want to rollback the jms message because something else went wrong. In that case you are kind of hosed because the jdbc work cannot be undone...so it better be idempotent.

      Comment


      • #4
        I can get away without XA because I can handle the case where the JDBC transaction commits, but the JMS transaction does not (the order of my commits is always JDBC first, followed by JMS commit). Within my JDBC transaction I have a message log to indicate that I've processed that message successfully. So if I ever see the message again (e.g. - JMS failed to commit) I know that I've already processed it.

        I specifically need to be able to treat the JDBC and JMS transactions as separate transactions that can be committed/rolled back independently in the error situations. Perhaps I need to do use programmatic transactions to do so. I was just hoping there was something I was missing.

        Comment


        • #5
          So I believe I figured out a simple way. Looking at AbstractMessageListenerContainer source it will always rollback the session if the SessionAwareMessageListener throws an exception.

          So the simple solution is to not throw an exception from the listener. However, I need to force the rollback of the jdbc transaction which is usually done by throwing an exception. Per Spring docs, I can force the rollback programmatically via:

          Code:
          TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
          Yes, this tightly couples my code to the spring framework, but that doesn't matter in this case and it greatly simplifies the logic and keeps things simple.

          In my quick testing, this successfully rolled back the current JDBC transaction while still committing the local JMS transaction.

          Comment


          • #6
            Hi Jacorob

            I am sure you are using batch commit while committing the messages to database.
            I am wondering how we can do a batch commit of messages using DefaultMessageListenerContainer.
            Everytime the listenermethod handleMessage() is called by the defaultmessagecontainer, a new JMS transaction is started and the transction is commited and ended as soon as the listener() method returns.
            That effectively indicates the message passed to the listener method need to be persisted to a persistent device (like database) before the listener method returns.
            If we dont store the message in a persistent device before the listener method returns , there is a chance that the message could be lost if the process crashes before
            committing to db or if the commmit to db itself fails.
            At the same time , I dont want to persist the message in the listener method as single commit to database will be a very expensive operation .
            I wanted to go for a batch commit to db with the gurantee that if the commit to db fails or if the process crashes before I commit I should be able to recover the messages when the process comes up next time.
            Last edited by dhimansu; Jun 14th, 2011, 08:10 PM.

            Comment

            Working...
            X