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);
}
_
トランザクションを命令的に管理するには、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);
回避策として、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();
}
これはおそらく理想的ではありませんが、現時点では機能します。
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
_を使用する場合は、JpaTransactionManager
andアクティブ化アノテーションベースのトランザクションの両方を設定する必要があります。 _<tx:annotation-driven />
_または_@EnableTransactionManagement
_のいずれかによる管理(サービスレイヤーでトランザクションを作成しようとしたときに、アクティベーションが不足しているように見えます)。
@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
を追加します