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()
.
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.
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.
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.
When the propagation is REQUIRES_NEW, Spring suspends the current transaction if it exists, and then creates a new one: @Transactional(propagation = Propagation.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With