10

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?

informatik01
  • 16,038
  • 10
  • 74
  • 104
letronje
  • 9,002
  • 9
  • 45
  • 53

4 Answers4

15

Yes Spring allows you to programmatically 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.

informatik01
  • 16,038
  • 10
  • 74
  • 104
cletus
  • 616,129
  • 168
  • 910
  • 942
  • 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 Jun 21 '09 at 13:55
  • 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 Jun 21 '09 at 14:48
  • Does the @Transactional annotation do anything when applied to a private method? – Andrew Swan Jun 22 '09 at 09:35
  • 2
    Yes, private isn't as private as it used to be. :) – cletus Jun 22 '09 at 10:12
8

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
  • 2,407
  • 16
  • 15
1

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
  • 305,152
  • 44
  • 369
  • 561
1

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

JARC
  • 5,288
  • 8
  • 38
  • 43