views:

1025

answers:

5

I am working on a java app that uses Spring IOC and JDBC Template classes. I have a DAO class that has 4 methods : m1() to m4(). m1 performs multiple inserts and updates on table t1, m2 on table t2, m3 on t3, etc.

The dao methods are used as follows:

while(true)
{
  //process & generate data

  dao.m1(data1);
  dao.m2(data2);
  dao.m3(data3);
  dao.m4(data4);

  //sleep
}

I want the db operations under the 4 consecutive method calls to be atomic, either all the 4 tables are updated successfully or none are. So, if there is an error while performing operations in m3(), i want to rollback all the changes(updates & inserts) performed in m2 & m1.

So does spring let you do it the following way ?

while(true)
{
  //process & generate data

  transaction = TransactionManager.createNewTransaction();

  transaction.start()

  try
  {
    dao.m1(data1);
    dao.m2(data2);
    dao.m3(data3);
    dao.m4(data4);
  }
  catch(DbUpdateException e)
  {
    transaction.rollBack();
  }

  transaction.end();

  // sleep

}

or are there better ways to do it ?

A: 

Yes, you can put those calls inside a method and specify your transactions declaratively.

You don't need to add that code - Spring can do it for you.

duffymo
+5  A: 

Yes Spring allows you to programatically control transactions.

Personally I prefer declarative transactions using annotations, which goes like this:

public void runBatchJob() {
  while (true) {
    // generate work
    doWork(unitOfWork);
  }
}

@transactional
private void doWork(UnitOfWork work) {
  dao.m1(data1);
  dao.m2(data2);
  dao.m3(data3);
  dao.m4(data4);
}

where the DAO functions are defined:

@Transactional
public void m1(Data data) {
  ...
}

This requires in your applicationContext.xml:

<tx:annotation-driven/>

Declarative transactions can be declared to require a transaction, require a new transaction, support transactions, etc. Rollback will occur when a block annotated with @Transactional throws a RuntimeException.

cletus
I don't believe you need to annotate the m1,m2,etc as @Transactional. If any throws an exception, everything already completed by doWork will be rolled back.
Chadwick
You don't need to in this context, no. But if they are doing updates, you should annotate them as requiring transactions, otherwise you may find yourself doing non-transactional updates if you call them outside the doWork() context.
cletus
Does the @Transactional annotation do anything when applied to a private method?
Andrew Swan
Yes, private isn't as private as it used to be. :)
cletus
+3  A: 

For completeness, the programmatic solution would be:

private TransactionTemplate transactionTemplate;

public setTransactionManager(PlatformTransactionManager transactionManager) {
  this.transactionTemplate = new TransactionTemplate(transactionManager);
}

...

while (true) {

  transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    protected void doInTransactionWithoutResult(TransactionStatus status) {
      try {
        dao.m1(data1);
        dao.m2(data2);
        dao.m3(data3);
        dao.m4(data4);
      } catch(DbUpdateException e) {
        status.setRollbackOnly();
      }
    }
  });
}
Maarten Winkels
+1  A: 

Spring can handle this all for you by using @Transactional as explained or in XML if you'd prefer.

The import thing to get right is the type of Transaction Propagation you want which all depends on your application.

By default a transaction will be started if one doesn't exist, and will re-use an existing transaction if one has already been started. This is the behavior you want if you want all 4 DAOs to be atomic.

Put @Transactional on a class which will manage the DAO methods called (MyService) - anything below this layer will now take part in that transaction boundary.

i.e:

@Transactional
public void m1(Data data) {
 ...
}

@Transactional
public void m2(Data data) {
 ...
}

Doing this in code is completely unnecessary.

See here for more info

JamesC
A: 

Hi ,

In above code ,if we are giving transaction to doWork method , why do need to give once again m1, m2 (DAO methods) , Because we are calling m1/m2 mothdos in doWork.

shekar