문제

I know multiple causes can trigger OptimistickLockException, but current test case demands a two transactions write into same data at same time to trigger the exception. More like to create read/write violation issue by parallel transactions. This is what I did:

  1. In the testing class, Create a private field to hold the transactions and some transactional methods:

    private List<Transaction> transactions = new ArrayList<Transaction>();
    
    // This method only generate transactions and assign
    // them to the member field
    @Transactional 
    public void retrieveTransactions(){
        transactions = generateTransactions(); // generateTransactions() is transactional
    }
    
    // Following two methods just write those
    // transactions into same summery, only the first one sleeps
    // during the process to make sure the second transaction
    // can occur in a parallel way
    @Transactional
    public void processTransactionsOne(){
        for(Transaction transaction:transactions){
            logger.info("====Transaction One=====");
            transaction.writeToSameSummary(); // writeToSameSummary() is transactional
            try {Thread.sleep(10000); } // sleep for 10 seconds before commit
            catch(InterruptedException ex) {}
        }
    }
    @Transactional
    public void processTransactionsTwo(){
        for(Transaction transaction:transactions){
            logger.info("====Transaction Two=====");
            transaction.writeToSameSummary(); // writeToSameSummary() is transactional
        }
    }
    
  2. in applicationContext.xml, I have made above three methods as scheduler, retrieveTransactions run every 60 seconds, processTransactionsOne and processTransactionsTwo run every 10 seconds

    <task:scheduled ref="testBeanName" method="retrieveTransactions" cron="*/60 * * * * *"/>
    <task:scheduled ref="testBeanName" method="processTransactionsOne" cron="*/10 * * * * *"/>
    <task:scheduled ref="testBeanName" method="processTransactionsTwo" cron="*/10 * * * * *"/>
    

However, I can never reproduce OptimistickLockException and the log showing up

====Transaction One=====
====Transaction One=====
====Transaction One=====
......
====Transaction Two=====
====Transaction Two=====
====Transaction Two=====

So the transaction are running as synchronized and never in parallel. So how can I (1) making parallel transactions and (2)reproduce OptimistickLockException

도움이 되었습니까?

해결책

To reproduce the optimistic locking exception, try to run code from a method that is not itself transactional. For example call a method that reads and modifies an entity, and returns a detached copy.

Then call the same method to modify the same entity, and return a new detached copy. The first copy now is stale compared to the database. Try to merge the first stale copy into a session, it will throw the optimistic locking exception.

This is some sample code for a versioned entity:

@Entity
public class MyEntity {
   ...
   @Version
   private Long version;
}

and this an example of a service that reads and returns an entity detached:

@Repository
public class SomeRepository {
    @PersistenceContext
    EntityManager em;

    @Transactional
    public MyEntity readAndModify(Long key) {
        MyEntity myEntity = em.find( key, MyEntity.class);
        myEntity.setSomeField(...); // this will trigger update via dirty checking
        return myEntity;
    }
}

This code in a test can then trigger the optimistic locking exception:

public someNonTransactionalTest() {
    //read and modify entity, get detached copy
    MyEntity detachedCopy1 = someRepository.readAndModify(1L); 

    //read and modify entity, get another detached copy - copy 1 is stale now
    MyEntity detachedCopy2 = someRepository.readAndModify(1L); 

    // try to merge the stale copy to a session, the optimistic locking exception is thrown
    detachedCopy1 = entityManager.merge(detachedCopy1);

    ... assertions ...
} 
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top