web-dev-qa-db-ja.com

Springで共有EntityManagerでトランザクションを手動で開始する方法は?

LocalContainerEntityManagerFactoryBeanインスタンスとしてEntityManagerがあります。

テーブル全体のコンテンツをすばやくドロップするには、次のコードを実行します。

_@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);
}
_
25
membersound

トランザクションを命令的に管理するには、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);
45
Jakub Kubrynski

回避策として、EntityManagerを使用して明示的に新しいEMFを作成し、トランザクションを手動で開始します。

@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();
}

これはおそらく理想的ではありませんが、現時点では機能します。

13
membersound

Spring Data 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_を使用する場合は、JpaTransactionManagerandアクティブ化アノテーションベースのトランザクションの両方を設定する必要があります。 _<tx:annotation-driven />_または_@EnableTransactionManagement_のいずれかによる管理(サービスレイヤーでトランザクションを作成しようとしたときに、アクティベーションが不足しているように見えます)。

7
Oliver Drotbohm

@Transactional注釈は、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を追加します

0
Manish Bansal