[SPRING] @Transactional 메서드에서 커밋을 수동으로 강제 실행하는 방법? [복제]
SPRING@Transactional 메서드에서 커밋을 수동으로 강제 실행하는 방법? [복제]
Spring / Spring-data-JPA를 사용하고 있습니다. 단위 테스트에서 수동으로 커밋을 수행해야합니다. 필자는 스레드가 생성되기 전에 지속되는 데이터를 사용해야하는 멀티 스레드 테스트를 수행하고 있습니다.
불행히도, 테스트가 @ 트랜잭션 트랜잭션에서 실행 중이면, 플러시로도 생성 된 스레드에 액세스 할 수 없습니다.
@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());
}
엔티티 관리자를 사용하여 시도했지만 시도 할 때 오류 메시지가 나타납니다.
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)
트랜잭션을 커밋하고 계속할 수있는 방법이 있습니까? 나는 commit ()을 호출 할 수있는 메소드를 찾을 수 없었다.
해결법
-
==============================
1.커밋 할 때만 호출되는 최대 절전 모드 이벤트 리스너를 테스트하는 동안 비슷한 사용 사례가있었습니다.
커밋 할 때만 호출되는 최대 절전 모드 이벤트 리스너를 테스트하는 동안 비슷한 사용 사례가있었습니다.
해결 방법은 코드가 REQUIRES_NEW로 주석 처리 된 다른 메서드에 지속되도록 래핑하는 것이 었습니다. (다른 클래스에서)이 방법은 새로운 트랜잭션이 생성되고 메소드가 반환되면 flush / commit가 실행됩니다.
이것은 다른 모든 테스트에 영향을 줄 수 있음을 명심하십시오! 따라서 적절하게 작성하거나 테스트가 실행 된 후에 정리할 수 있도록해야합니다.
-
==============================
2.Spring의 TransactionTemplate을 사용하여 트랜잭션을 프로그래밍 방식으로 제어하지 않는 이유는 무엇입니까? 또한 각 "트랜잭션 블록"에 @Transactional 메서드가 있으므로 트랜잭션을 프로그래밍 방식으로 제어하도록 선택할 수 있으므로 코드를 다시 구성 할 수 있습니다.
Spring의 TransactionTemplate을 사용하여 트랜잭션을 프로그래밍 방식으로 제어하지 않는 이유는 무엇입니까? 또한 각 "트랜잭션 블록"에 @Transactional 메서드가 있으므로 트랜잭션을 프로그래밍 방식으로 제어하도록 선택할 수 있으므로 코드를 다시 구성 할 수 있습니다.
또한 runnable에 @Transactional 주석은 작동하지 않습니다 (aspectj를 사용하지 않는 한) runnables는 봄까지 관리되지 않습니다!
@RunWith(SpringJUnit4ClassRunner.class) //other spring-test annotations; as your database context is dirty due to the committed transaction you might want to consider using @DirtiesContext public class TransactionTemplateTest { @Autowired PlatformTransactionManager platformTransactionManager; TransactionTemplate transactionTemplate; @Before public void setUp() throws Exception { transactionTemplate = new TransactionTemplate(platformTransactionManager); } @Test //note that there is no @Transactional configured for the method public void test() throws InterruptedException { final Contract c1 = transactionTemplate.execute(new TransactionCallback<Contract>() { @Override public Contract doInTransaction(TransactionStatus status) { Contract c = contractDOD.getNewTransientContract(15); contractRepository.save(c); return c; } }); ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; ++i) { executorService.execute(new Runnable() { @Override //note that there is no @Transactional configured for the method public void run() { transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { // do whatever you want to do with c1 return null; } }); } }); } executorService.shutdown(); executorService.awaitTermination(10, TimeUnit.SECONDS); transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { // validate test results in transaction return null; } }); }
}
-
==============================
3.TransactionTemplate의 익명의 익명 내부 클래스 사용법이 멋지게 보이지 않는다는 것을 알고 있지만, 어떤 이유로 트랜잭션 메소드 IMHO를 테스트하고자 할 때 가장 유연한 옵션입니다.
TransactionTemplate의 익명의 익명 내부 클래스 사용법이 멋지게 보이지 않는다는 것을 알고 있지만, 어떤 이유로 트랜잭션 메소드 IMHO를 테스트하고자 할 때 가장 유연한 옵션입니다.
일부 경우 (애플리케이션 유형에 따라 다름), Spring 테스트에서 트랜잭션을 사용하는 가장 좋은 방법은 테스트 메소드에서 꺼져있는 트랜잭션이다. 왜? @Transactional은 많은 위양성 검사를 유발할 수 있기 때문에. 이 샘플 기사에서 자세한 내용을 볼 수 있습니다. 이러한 경우 TransactionTemplate은 트랜잭션 경계를 제어 할 때 트랜잭션 경계를 제어하는 데 완벽 할 수 있습니다.
from https://stackoverflow.com/questions/24338150/how-to-manually-force-a-commit-in-a-transactional-method by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] <spring : url />을 <a> 태그와 함께 사용하는 방법? (0) | 2018.12.18 |
---|---|
[SPRING] 고유 한 bean을 가진 스프링 autowiring : 봄 예상되는 단일 일치하는 bean은 찾았지만 2 (0) | 2018.12.18 |
[SPRING] Spring MVC - AngularJS - 파일 업로드 - org.apache.commons.fileupload.FileUploadException (0) | 2018.12.18 |
[SPRING] 다중 파일 업로드 봄 부팅 (0) | 2018.12.18 |
[SPRING] 데이터베이스에 메타 데이터를 지속시키지 않고 Spring-Batch? (0) | 2018.12.18 |