복붙노트

[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. ==============================

    1.커밋 할 때만 호출되는 최대 절전 모드 이벤트 리스너를 테스트하는 동안 비슷한 사용 사례가있었습니다.

    커밋 할 때만 호출되는 최대 절전 모드 이벤트 리스너를 테스트하는 동안 비슷한 사용 사례가있었습니다.

    해결 방법은 코드가 REQUIRES_NEW로 주석 처리 된 다른 메서드에 지속되도록 래핑하는 것이 었습니다. (다른 클래스에서)이 방법은 새로운 트랜잭션이 생성되고 메소드가 반환되면 flush / commit가 실행됩니다.

    이것은 다른 모든 테스트에 영향을 줄 수 있음을 명심하십시오! 따라서 적절하게 작성하거나 테스트가 실행 된 후에 정리할 수 있도록해야합니다.

  2. ==============================

    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. ==============================

    3.TransactionTemplate의 익명의 익명 내부 클래스 사용법이 멋지게 보이지 않는다는 것을 알고 있지만, 어떤 이유로 트랜잭션 메소드 IMHO를 테스트하고자 할 때 가장 유연한 옵션입니다.

    TransactionTemplate의 익명의 익명 내부 클래스 사용법이 멋지게 보이지 않는다는 것을 알고 있지만, 어떤 이유로 트랜잭션 메소드 IMHO를 테스트하고자 할 때 가장 유연한 옵션입니다.

    일부 경우 (애플리케이션 유형에 따라 다름), Spring 테스트에서 트랜잭션을 사용하는 가장 좋은 방법은 테스트 메소드에서 꺼져있는 트랜잭션이다. 왜? @Transactional은 많은 위양성 검사를 유발할 수 있기 때문에. 이 샘플 기사에서 자세한 내용을 볼 수 있습니다. 이러한 경우 TransactionTemplate은 트랜잭션 경계를 제어 할 때 트랜잭션 경계를 제어하는 ​​데 완벽 할 수 있습니다.

  4. from https://stackoverflow.com/questions/24338150/how-to-manually-force-a-commit-in-a-transactional-method by cc-by-sa and MIT license