web-dev-qa-db-ja.com

Spring boot + Hibernate + JPA利用可能なトランザクションEntityManagerなし

スプリングブート1.2.3.RELEASEバージョンをJPA with hibernateで使用しています。次の例外が発生しています

org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.Java:410) ~[EntityManagerFactoryUtils.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.Java:223) ~[HibernateJpaDialect.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.Java:417) ~[AbstractEntityManagerFactoryBean.class:4.1.6.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.Java:59) ~[ChainedPersistenceExceptionTranslator.class:4.1.6.RELEASE]
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.Java:213) ~[DataAccessUtils.class:4.1.6.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.Java:147) ~[PersistenceExceptionTranslationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.Java:122) ~[CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.class:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.Java:92) [ExposeInvocationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.Java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.Java:207) [JdkDynamicAopProxy.class:4.1.6.RELEASE]
at com.Sun.proxy.$Proxy110.deleteByCustomerId(Unknown Source) ~[na:na]

Caused by: javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.Java:275) ~[SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.class:4.1.6.RELEASE]
at com.Sun.proxy.$Proxy102.remove(Unknown Source) ~[na:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$DeleteExecution.doExecute(JpaQueryExecution.Java:270) ~[JpaQueryExecution$DeleteExecution.class:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.Java:74) ~[JpaQueryExecution.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.Java:97) ~[AbstractJpaQuery.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.Java:88) ~[AbstractJpaQuery.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.Java:395) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.Java:373) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]

以下は私のプログラム構造です
構成クラス

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableTransactionManagement
public class WSApplication {
    public static void main(final String[] args) {
        SpringApplication.run(WSApplication.class, args);
    }
}

@Entity
@Table(Orders)
public class Order {
    @id
    @GeneratedValue
    private long id;

    @Column(name = "customerId")
    private Long customerId;

    // getter & setter methods
    // equals & hashCode methods
}

public interface OrderRepository extends JpaRepository<Order, Long> {

    List<Order> findByCustomerId(Long customerId);

    // 4- @Transactional works fine
    void deleteByCustomerId(Long cusotmerId);

}

public class OrderService {

    @Autowired
    private OrderRepository repo;

    // 3- @Transactional works fine
    public void deleteOrder(long customerId){
        //1- throws exception
        repo.deleteByCustomerId(customerId); 

        //2- following works fine
        //repo.delete(repo.findByCustomerId(customerId).get(0));
    }

}

上記のサービスクラスコードで、2が機能し、1が例外をスローする理由を誰かに教えてもらえますか?.

ありがとう

14
amique

最初に、 Spring-Data JPA Documentation を引用して、deleteメソッドがあなたのケースで機能する理由を正当化します(つまり、オプション2 )。

リポジトリインスタンスのCRUDメソッドは、デフォルトでトランザクション対応です。読み取り操作の場合、トランザクション構成readOnlyフラグがtrueに設定され、他のすべてはプレーン@Transactionalで構成されているため、デフォルトのトランザクション構成が適用されます。詳細については、CrudRepositoryのJavaDocを参照してください

deleteメソッドは、実際にはCrudRepositoryのメソッドです。リポジトリはJpaRepositoryを拡張します。これはCrudRespositoryを拡張するため、CrudRepositoryインターフェースに属し、上記の見積もりによるとトランザクションです。

セクション トランザクションクエリメソッド を読んだ場合、それはオプション4と同じであることがわかります。リポジトリのすべてのメソッドにカスタムトランザクション動作を適用します。また、ドキュメントの例61は、オプション3と同じシナリオを示しています。

ここでは、JDBCロジックを使用していないことに注意してください。この場合、データベースはトランザクションを処理しますが、ORMベースのフレームワーク内で処理します。 ORMフレームワークでは、オブジェクトキャッシュとデータベース間の同期をトリガーするためにトランザクションが必要です。したがって、deleteByCustomerIdのようなORMロジックを実行するメソッドのトランザクションコンテキストを認識して提供する必要があります。

デフォルトでは、@Transactional(つまり、パラメーターなし)は、伝搬モードをREQUIREDに設定し、readOnlyフラグをfalseに設定します。アノテーションが付けられたメソッドを呼び出すと、トランザクションが存在しない場合はトランザクションが初期化されます。これが、@ LucasSaldanhaの回避策の理由です(例と同じ)ファサードを使用して複数のリポジトリ呼び出しのトランザクションを定義する )およびオプション4が機能します。そうでなければ、トランザクションがなければ、オプション1のスローされた例外に陥ります。

13
Guillermo

わかりました、それを機能させる方法を見つけました。

@Transactionalアノテーション(org.springframework.transaction.annotation.Transactional)をdeleteOrderメソッドでOrderService

@Transactional
public void deleteOrder(long customerId){
    repo.deleteByCustomerId(customerId);
}

2番目の方法が機能する理由は本当にわかりません。 CrudRepositoryインターフェースからの直接的な方法であるため、アトミックに実行する方法を知っていると思います。

前者はdeleteByCustomerIdの呼び出しです。この呼び出しは、指定されたIDを持つ顧客を見つけるために処理され、それを削除します。何らかの理由で、明示的なトランザクションを使用します。

繰り返しますが、それは単なる推測です。私はいくつかの春の開発者に連絡してみて、おそらく問題を開いてこの動作を確認しようと思います。

それが役に立てば幸い!

リファレンス: http://spring.io/guides/gs/managing-transactions/

2
Lucas Saldanha

search()メソッドにNo transactional EntityManager availableでアノテーションを付けた後でも、@Transactional例外が発生します。

私は このチュートリアル に従い、Spring BootでHibernate検索を設定する方法を説明しました。

私にとっての問題は、hibernate-search-ormへの依存関係が異なることでした。問題なく私のために働いた依存関係は

compile("org.hibernate:hibernate-search-orm:5.7.0.Final")

これをgradleビルドファイルに追加した後、すべてが期待どおりに機能しました。

これが他の誰かにも役立つことを願っています。

2
Patrick