2

I have a couple of questions about Transactions in Spring if you may. Let's suppose i have this DAO class :

public class MyDAO {

    /**
    * verifies if a certain record in DB contains 'True' in a certain Column named publishFlag
    */
    @Transactional
    public bloolean isBeingPublished(Long recordID){
    ...
    }

    /**
    * sets the record's publishFlag column to true indicating that it's being published
    */
    @Transactional
    public boolean setBeingPublished(Long recordID){
    ...
    }

}

And the following class using it :

public class MyClass {

    @Autowired
    MyDAO dao;

    public void publishRecords(List<Long> ids){

        for(Long id : ids){
            if(!dao.isBeingPublished(id)){
                dao.setBeingPublished(id);
                //do something to publish the record
            }
        }

    }
}

My questions are :

  • First of all, will the !dao.isBeingPublished(id) and dao.setBeingPublished(id) be executed in the same transaction or in separate ones?

  • Second question's about concurrency, Multiple MyClass instances can be created and concurrent calls to the publishRecord method can occur, so two concurrent calls to !dao.isBeingPublished(id) might both give the same result and thus making the record published twice! I would consider making the publishRecords synchronized but the application may be deployed on multiple servers which renders the synchronized declaration useless, hence my question about transactions since the database is the only shared resource between the apps deployed on those servers.

What would be the solution to my problem exactly? I read about spring's transaction propagation and found out that REQUIRES_NEW would create a new transaction even if one is currently being executed, but still, I just can't see how that's going to be a solution to my problem.

Thank you in advance for your help.

snajahi
  • 900
  • 2
  • 14
  • 26

2 Answers2

3

Few things need consider, DAO is focus on operation on single entity, and service is focus on operation of one or more entities, so the transaction should put on service layer, so you can reuse DAO's operation without any transaction, but let service to decide when start and end transaction

  1. It is not in single transaction, but two separate transaction.
  2. That is the problem concurrency issue with your current design, see the following suggestion.

Interface

public interface MyClass {
    public void publishRecords(List<Long> ids);
}

Implementation

@Service
@Transactional(readOnly = false)
class DefaultMyClass implements MyClass  {

    @Autowired
    MyDAO dao;

    // single transaction
    @Override
    public void publishRecords(List<Long> ids) {
        for(Long id : ids){
            if(!dao.isBeingPublished(id)){
                dao.setBeingPublished(id);
                //do something to publish the record
            }
        }
    }
}

DAO

class MyDAO {

    public bloolean isBeingPublished(Long recordID){
        // bigbang
    }

    public boolean setBeingPublished(Long recordID){
        // bigbang
    }
}

Using the above design, both problems are being resolved.

Pau Kiat Wee
  • 9,485
  • 42
  • 40
  • thanks for the reply, the suggestion would actually make them run in the same transaction, but still, the **single transactions** in which `publishRecords` is run would be separate no? before a transaction A **commits** its changes to DB, another transaction B could start and the concurrency issue would remain – snajahi Apr 20 '12 at 14:59
  • 2
    @K-SaMa you can implement optimistic locking in JPA using @ Version annotation, see more [here](http://stackoverflow.com/questions/2572566/java-jpa-version-annotation) – Pau Kiat Wee Apr 20 '12 at 15:06
1

First of all, will the !dao.isBeingPublished(id) and dao.setBeingPublished(id) be executed in the same transaction or in seperate ones?

Unless there's a method annotated with @Transactional further up the stack, they will be occurring in separate transactions, so yes you will have a potential for a race condition.

If I were you, I would toss the isBeingPublished and setBeingPublished in favor of a single @Transactional publishIfPossible method that returns a boolean value of whether it was able to acquire the database row lock and do the publish operation.

stevevls
  • 10,675
  • 1
  • 45
  • 50
  • your suggestion seams quite interesting, nonetheless if I were to create such a method, first call to it would begin a transaction and this method would try to publish the record and then commits changes to DB in order for them to be visible; What about another call to the method creating another transaction before the first one has committed its changes? that would also make the record published twice no? – snajahi Apr 20 '12 at 15:03
  • If you have the proper transaction isolation configured (and your DB supports it...this won't work for example with MySQL MyISM tables), the first transaction will hold a row-level lock and the second transaction will block until that row-level lock is released. In general, the DB is really good at managing locking and concurrency, so it's better to let it handle the details than to try to do it yourself! – stevevls Apr 20 '12 at 15:15
  • the DB Management System is oracle, so I assume support won't be an issue; I'll take a look at Transaction isolation thanks. – snajahi Apr 20 '12 at 15:27