Announcement Announcement Module
Collapse
No announcement yet.
Programmatically rollback a declarative transaction Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Programmatically rollback a declarative transaction

    I want to use declarative transactions AND I want to programatically issue a rollback. Yes, I want my cake and eat it too!

    Here's my reasoning. I want to control transactions at the business-presentation layer boundary because the decision to rollback/commit is a business one. Of course serious technical exceptions should issue a rollback, but sometimes its a business reason (i.e. not enough funds in an account). In addition, I want to communicate back to the presentation layer that there was a problem, the type of problem and some data (i.e. account number).

    In the EJB world I would have called setRollbackOnly, then set an error code and data in a response object to send back to the presentation layer. I would have defined the transaction declaratively, yet managed the rollback programmatically. In addition, a RuntimeException would have have cause a rollback if I decided to let the exception slip out to the EJB container.

    How can I do that same? As far as I can tell the only way to rollback using Spring declarative transaction is to send back an exception. If I do that, then I need to create a new exception (if I don't want to send back a Spring DataAccessException) which is expensive. I need to put my data in this new exception. I would be treating an exception as a value object.

    How can I programatically rollback when I detect a business reason to do so without throwing an exception?

  • #2
    How can I do that same? As far as I can tell the only way to rollback using Spring declarative transaction is to send back an exception. If I do that, then I need to create a new exception (if I don't want to send back a Spring DataAccessException) which is expensive. I need to put my data in this new exception. I would be treating an exception as a value object.
    Well isn't this the way to go? I mean you stated that deciding to rollback a transaction is a business issue in your situation. I consider rolling back a transaction to be exceptional, so I would go for an exception. (what do you exactly mean by 'create a new exception, which is expensive' by the way?)

    In cases this isn't suitable there is another technic I sometimes used and still would use. If I need to control the transactional behaviour directly, I mostly surround the particular logic using the PROPAGATION_REQUIRED setting. By using propagation-required I can create a transaction, roll it back and see also the original surrounding transaction to be rolled back to. Even in the case there isn't an active transaction still being defined, the code would still work.

    I us this mostly in complex business logic, which uses application level transactional mechanism (like version control or timestamp flags). But using propagation-required you still support a surrounding big transaction.

    If you do not want to use propagtion-required transactions and tackle the current transaction yourself you might start by looking at the TransactionInterceptor of the org.springframework.transaction.interceptor package first. Following the code, you will notice, that everything comes down to the following code-part:

    Code:
    TransactionAttribute transAtt = this.transactionAttributeSource.getTransactionAttribute(method, targetClass);
    TransactionInfo txInfo = new TransactionInfo(transAtt, method);
    ...    xInfo.newTransactionStatus(this.transactionManager.getTransaction(transAtt));
    Seeing this you have to check the particular transaction manager implementation you are about to use. You need to take a close look at the PlatformTransactionManager.getTransaction(...) implementation of that particular transaction manager. So you tightly couple the code to a certain transactional scenario. And that's why I would suggest to avoid this by all means.


    So to summarize:

    1. I would go for a special exception.
    2. If this isn't suitable, I would create a custom transaction using the propagation-required setting.


    Cheers,

    Martin (Kersten)

    Comment


    • #3
      Thanks for your reply.

      what do you exactly mean by 'create a new exception, which is expensive' by the way?
      e.g. I get some data, update some stuff and do some calculations. The calcs show that there is a business rule indicating that I need to rollback the update. I never got an exception from persistence layer, it was a business rule that told me that I need to rollback. Therefore I need to create an exception to tell Spring to rollback.

      Of course I would try to avoid the update unless the business rule passed, but I always like to rollback as a defensive move.

      http://www-106.ibm.com/developerwork...perf07303.html

      http://www-106.ibm.com/developerwork...perf02104.html

      Comment


      • #4
        By using propagation-required I can create a transaction, roll it back and see also the original surrounding transaction to be rolled back to. Even in the case there isn't an active transaction still being defined, the code would still work.
        Are you saying that you would create a programatic transaction and roll it back?

        Comment


        • #5
          Exactly. Since you specify the transaction to apply to meet propagation-required, the transaction you are actually controlling, is the already existing surrounding transaction (indirectly of cause).

          Example:

          1. declarative (done by TransactionInterceptor): begin transaction -> transaction created

          2. call business method
          2a. create programatically a transaction specifying propagation-required
          -> no new transaction is created.
          2b. roll back programmatically optained transaction
          -> causes the original transaction to be also rolled back (or at least setRollbackOnly is set to true)


          I guess this would be a quite suitable solution for your problem.


          Cheers,

          Martin (Kersten)

          Comment


          • #6
            e.g. I get some data, update some stuff and do some calculations. The calcs show that there is a business rule indicating that I need to rollback the update. I never got an exception from persistence layer, it was a business rule that told me that I need to rollback. Therefore I need to create an exception to tell Spring to rollback.
            Now I understand. That makes scense. So you are doing something like a post-conditional test within your business rule. This is quite interesting.

            By using propagation-required you are forming a nested transaction which is vital for the success of the surrounding (parent) transaction. But beware to check first, if the implementation of the particular transaction manager is really creating a nested transaction or just simply warping the existing one.

            What particular transactional manager(s) do you use?


            Cheers,

            Martin (Kersten)

            Comment


            • #7
              Very interesting. Thanks.

              Comment


              • #8
                What particular transactional manager(s) do you use?
                This is the interesting part because it really shows the power of Spring. I have two host enviroments! One on a mobile device and another on a server. The user can use the application locally, or server side on WebSphere. The client points to a server-side URL while connected, but to a local URL when not connected. The app resides on the client and the server.

                Thanks to Spring, I can reuse my entire code base in both env. I simply use a the appropriate transaction manager for the respective env. When the client accesses the app by connecting to the server, the code base on the server will use WAS JTA. When the client is off-line (and points to the local app), the app is using the database's (DB2e) transaction manager.

                Same deal for connection pooling. There will be build scripts for a server side build and another one for build destined for the client.

                Comment


                • #9
                  There will be build scripts for a server side build and another one for build destined for the client.
                  So you shouldn't rely on the particular transaction manager implementations. I am not used to the current implementations but I guess the transaction status objects where also stored using the ThreadLocal prattern. But this should be answered by a core developer, I guess. Or even better you review the parts of the code-base and tell me your findings. ;-)


                  But thinking even further:

                  How often do you expect your business rule to fail? Check out this one:

                  Code:
                  public void rollbackCurrentTransaction() {
                      TransactionStatus transaction=beginNestedTransaction();
                      transaction.setRollbackOnly();
                      end transaction(); 
                  }
                  So you need a nested transaction only in the case of a roll back. So you actually don't need to surround the whole business rules. I think this is truely obvious but I am not used to this, I only used explicit transaction control to surround business-logic. So I will write this down. It is a nice option to have in the Spring toolbox 8-).


                  Cheers,

                  Martin (Kersten)

                  PS: Can you tell me a bit more about your use-case. How often do you expect the transaction to fail because of a business rule (validation) failure? How many transactions do you have per minutes? It is really an interesting use-case scenario, so I would love to hear a more detailed description... . Thanks!

                  Comment


                  • #10
                    In one of the projects I'm involved in we have had the need to set the transaction status to rollback, but continue execution so that the method can return a response object (similar to your situation).

                    You can use Spring's TransactionInterceptor to programatically rollback a transaction:
                    Code:
                    TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

                    Comment


                    • #11
                      TransactionAspectSupport.currentTransactionStatus( ).setRollbackOnly();

                      Comment

                      Working...
                      X