복붙노트

[SPRING] 스프링의 공유 EntityManager에서 트랜잭션을 수동으로 시작하는 방법은 무엇입니까?

SPRING

스프링의 공유 EntityManager에서 트랜잭션을 수동으로 시작하는 방법은 무엇입니까?

EntityManager 인스턴스로 LocalContainerEntityManagerFactoryBean이 있습니다.

전체 테이블의 내용을 빠르게 삭제하려면 다음 코드를 실행하고 싶습니다.

@Service
public class DatabaseService {
    @Autowired
    private EntityManager em;

    @Transactional
    public void clear() {
        em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
    }
}

결과:

ERROR org.springframework.integration.handler.LoggingHandler: javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:71)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:708)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

이 변경을하면 :

public void clear() {
    em.getTransaction().begin();
    em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
}

결과:

ERROR org.springframework.integration.handler.LoggingHandler: java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:245)
    at com.sun.proxy.$Proxy84.getTransaction(Unknown Source)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:708)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

나는 또한 spring-data-jpa를 시도했지만 실패했다.

public interface MyRepository extends CrudRepository<MyEntity, Integer> {
    @Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true)
    @Modifying
    public void clear();
}

그렇다면 어떻게 트랜잭션을 생성하고 공유 된 스프링 컨텍스트에서 잘라내기를 실행할 수 있습니까?

Spring 애플리케이션은 다음을 사용하여 시작된다. SpringApplication.run (AppConfig.class, args); 데 :

@Bean
public JpaTransactionManager transactionManager() {
    return new JpaTransactionManager(emf);
}

해결법

  1. ==============================

    1.트랜잭션을 명령 적으로 관리하려면 TransactionTemplate 객체를 사용해야합니다.

    트랜잭션을 명령 적으로 관리하려면 TransactionTemplate 객체를 사용해야합니다.

    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                em.createNativeQuery("TRUNCATE TABLE MyTable).executeUpdate();
            }
        });
    

    TransactionTemplate을 만들려면 주입 된 PlatformTransactionManager 만 사용하십시오.

    transactionTemplate = new TransactionTemplate(platformTransactionManager);
    

    그리고 만약 당신이 새로운 트랜잭션을 사용하기를 원한다면

    transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    
  2. ==============================

    2.임시 해결책으로 EMF를 사용하여 명시 적으로 새 EntityManager를 만들고 트랜잭션을 수동으로 시작했습니다.

    임시 해결책으로 EMF를 사용하여 명시 적으로 새 EntityManager를 만들고 트랜잭션을 수동으로 시작했습니다.

    @Autowired
    private EntityManagerFactory emf;
    
    public void clearTable() {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        em.createNativeQuery("TRUNCATE TABLE MyTable).executeUpdate();
        tx.commit();
        em.close();
    }
    

    아마 이상적은 아니지만 지금 당장은 작동합니다.

  3. ==============================

    3.스프링 데이터 JPA는 자동으로 트랜잭션에서 CRUD 메소드를 실행합니다 (트랜잭션 관리자를 제외하고 아무 것도 설정하지 않아도 됨). 쿼리 메소드에 트랜잭션을 사용하려면 다음과 같이 @Transactional을 추가하면됩니다.

    스프링 데이터 JPA는 자동으로 트랜잭션에서 CRUD 메소드를 실행합니다 (트랜잭션 관리자를 제외하고 아무 것도 설정하지 않아도 됨). 쿼리 메소드에 트랜잭션을 사용하려면 다음과 같이 @Transactional을 추가하면됩니다.

    interface MyRepository extends CrudRepository<MyEntity, Integer> {
    
      @Transactional
      @Modifying
      @Query(value = "TRUNCATE TABLE MyTable", nativeQuery = true)
      void clear();
    }
    

    더 일반적으로, 여기서 선언 한 내용은 CrudRepository.deleteAll ()과 논리적으로 동일합니다. 단, 선언문이 JPA 수준의 계단식을 따르지 않는다는 점만 다릅니다. 그래서 나는 그것이 정말로 당신이 의도 한 것이 었는지 궁금해했습니다. Spring Boot를 사용한다면, 활성화와 트랜잭션 관리자 설정이 당신을 대신해야합니다.

    서비스 레벨에서 @Transactional을 사용하려면 JpaTransactionManager를 설정하고 또는 @EnableTransactionManagement를 통해 주석 기반 트랜잭션 관리를 활성화해야합니다 (활성화가 누락 된 부분 인 것처럼 보입니다 서비스 계층에서 트랜잭션을 생성합니다.

  4. ==============================

    4.@ 트랜잭션 주석은 Dao 메소드에는 적용하지 말고 서비스 메소드에 적용해야합니다. 코드에서 DatabaseService가 서비스이지만 EntityManger를 서비스 내에 삽입한다고해서 말이되는 것은 아닙니다.

    @ 트랜잭션 주석은 Dao 메소드에는 적용하지 말고 서비스 메소드에 적용해야합니다. 코드에서 DatabaseService가 서비스이지만 EntityManger를 서비스 내에 삽입한다고해서 말이되는 것은 아닙니다.

    올바른 구현 방법은 아래와 같이 Dao를 만드는 것입니다.

    @Repository
    public class DatabaseDao {
        @PersistenceContext
        private EntityManager em;
    
        public void clear() {
            em.createNativeQuery("TRUNCATE TABLE MyTable").executeUpdate();
        }
    }
    

    그런 다음 @Transactional 주석을 사용하여 서비스 메소드에서 dao 메소드를 호출하십시오.

    @Service
    public class DatabaseService {
        @Autowired
        private DatabaseDao dao;
    
        @Transactional
        public void clear() {
            dao.clear();
        }
    }
    

    또한 Configuration 클래스에 @EnableTransactionManagement를 추가하십시오.

  5. from https://stackoverflow.com/questions/26606608/how-to-manually-start-a-transaction-on-a-shared-entitymanager-in-spring by cc-by-sa and MIT license