Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manually force a commit in a @Transactional method? [duplicate]

I'm using Spring / Spring-data-JPA and find myself needing to manually force a commit in a unit test. My use case is that I am doing a multi-threaded test in which I have to use data that is persisted before the threads are spawned.

Unfortunately, given that the test is running in a @Transactional transaction, even a flush does not make it accessible to the spawned threads.

   @Transactional       public void testAddAttachment() throws Exception{         final Contract c1 = contractDOD.getNewTransientContract(15);         contractRepository.save(c1);          // Need to commit the saveContract here, but don't know how!                         em.getTransaction().commit();          List<Thread> threads = new ArrayList<>();         for( int i = 0; i < 5; i++){             final int threadNumber = i;              Thread t =  new Thread( new Runnable() {                 @Override                 @Transactional                 public void run() {                     try {                         // do stuff here with c1                          // sleep to ensure that the thread is not finished before another thread catches up                         Thread.sleep(1000);                     } catch (InterruptedException e) {                         // TODO Auto-generated catch block                         e.printStackTrace();                     }                 }             });             threads.add(t);             t.start();         }          // have to wait for all threads to complete         for( Thread t : threads )             t.join();          // Need to validate test results.  Need to be within a transaction here         Contract c2 = contractRepository.findOne(c1.getId());     } 

I've tried using the entity manager to, but get an error message when I do:

org.springframework.dao.InvalidDataAccessApiUsageException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead; nested exception is java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead     at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:293)     at org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:33) 

Is there any way to commit the transaction and continue it? I have been unable to find any method that allows me to call a commit().

like image 350
Eric B. Avatar asked Jun 21 '14 04:06

Eric B.


People also ask

How do I force a commit in spring boot?

Commit a transaction by calling the commit() method on the Connection interface. This tells your database to perform all required consistency checks and persist the changes permanently. Rollback all operations performed during the transaction by calling the rollback() method on the Connection interface.

Does @transactional work on private methods?

The answer your question is no - @Transactional will have no effect if used to annotate private methods. The proxy generator will ignore them. When using proxies, you should apply the @Transactional annotation only to methods with public visibility.

What does the @transactional annotation do?

The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.

What is @transactional propagation propagation Requires_new?

When the propagation is REQUIRES_NEW, Spring suspends the current transaction if it exists, and then creates a new one: @Transactional(propagation = Propagation.


1 Answers

I had a similar use case during testing hibernate event listeners which are only called on commit.

The solution was to wrap the code to be persistent into another method annotated with REQUIRES_NEW. (In another class) This way a new transaction is spawned and a flush/commit is issued once the method returns.

Tx prop REQUIRES_NEW

Keep in mind that this might influence all the other tests! So write them accordingly or you need to ensure that you can clean up after the test ran.

like image 60
Martin Frey Avatar answered Sep 23 '22 08:09

Martin Frey