Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get transactionManager reference in Spring Programmatic transaction

I want to use spring programmatic transaction in JMS application that receive a message from queue. At the same time, I want to include the queue in transactional scope.

Using Spring DefaultMessageListnereContainer and injecting transaction manager in it. However, not sure how will I get the reference of this transaction to programmatically commit or rollback?


I read and understand "processing messages within transactions" here http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/jms.html#jms-tx-participation

My 2 requirements are - 1) XA transactions hence injecting JtaTransactionManager, 2) Use programmatic transactions - here I need help how to get reference to the transaction started by spring in the code so that I can programmatically handle transactions

like image 560
Nitin Gaur Avatar asked Jan 27 '12 13:01

Nitin Gaur


2 Answers

You first have to inject org.springframework.transaction.PlatformTransactionManager - it is an ordinary bean like all the others:

@Resource
private PlatformTransactionManager transactionManager;

Now you can use it together with TransactionTemplate:

final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallback<String>() {
    @Override
    public Object doInTransaction(TransactionStatus status) {
        transactionManager.rollback(status);
        return ":-(";
    }
});

Quite a lot of code, so here is how you should do this:

@Transactional
public void onMessage(Message message) {
    //rollback:
    throw new HoustonException("We've got a problem!");
}

If you throw a RuntimeException from @Transactional method, it will automatically rollback that transaction. Otherwise it will be committed.

Note that this doesn't mean that JMS and database is working on the same transaction! When you throw an exception the JMS broker will try to redeliver the message, however chances are that the broker will fail after the DB transaction was committed. If you need to be 100% sure that both JMS and DB updates are atomic, you need distributed transaction manager.

like image 57
Tomasz Nurkiewicz Avatar answered Sep 19 '22 08:09

Tomasz Nurkiewicz


Spring abstracts underlying transaction manger using PlatformTransactionManager.
At runtime we can inject platform specific transaction manager shown in the following image.

enter image description here

We can get Transaction Manager reference in spring programmatic approach the following two ways:

  1. Using interface PlatformTransactionManager : We have to explicitly create a TransactionDefinition and TransactionStatus object.

For Example:

       @Resource
       private PlatformTransactionManager transactionManager;
       public void create(String name, Integer age, Integer marks, Integer year){

          TransactionDefinition def = new DefaultTransactionDefinition();
          TransactionStatus status = transactionManager.getTransaction(def);

      try {
             //jmsTemplate statements
             transactionManager.commit(status);
          } catch (DataAccessException e) {
             System.out.println("Error in creating record, rolling back");
             transactionManager.rollback(status);
             throw e;
          }
  1. Using class TransactionTemplate :

We do not have to explicitly create a TransactionDefinition and TransactionStatus object. The TransactionTemplate provides a callback method called execute where we can add our business logic that we want to wrap in a transaction.

There are two types of callback methods that we can use to wrap our code i.e TransactionCallbackWithoutResult and TransactionCallback(T)

For Example:

@Resource
private TransactionTemplate transactionTemplate;

public void deleteUser(final int uid) {
  transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    protected void doInTransactionWithoutResult(TransactionStatus paramTransactionStatus) {
    try{
        //statements
    }catch (Exception e) {
      //use this to rollback exception in case of exception
      paramTransactionStatus.setRollbackOnly();
    }
    }
  });

}

public int insertUser(final User user) {
//use TransactionCallback handler if some result is returned
return  transactionTemplate.execute(new TransactionCallback<Integer>() {
  public Integer doInTransaction(TransactionStatus paramTransactionStatus) {
    //statements
   }
 });
}
like image 24
Premraj Avatar answered Sep 20 '22 08:09

Premraj