49

Spring Bean コンテナーを使用せずに、DAO プロキシー (別名リポジトリー) を生成できるように、Spring Data JPA オブジェクトを手動で接続しようとしています。

必然的に、なぜこれをやりたいのかと聞かれます: 私たちのプロジェクトは既に Google Guice を使用しており (そして GWT で Gin を使用している UI で)、別の IoC コンテナー構成を維持したくない、またはプルインしたくないためです。結果として生じるすべての依存関係。Guice の を使用できる可能性があることはわかってSpringIntegrationいますが、これは最後の手段です。

オブジェクトを手動で接続するためにすべてが利用できるようですが、十分に文書化されていないため、苦労しています。

Spring Data ユーザー ガイドによると、リポジトリ ファクトリをスタンドアロンで使用することが可能です。残念ながら、この例はRepositoryFactorySupportどちらが抽象クラスであるかを示しています。いくつかの検索の後、私は見つけることができましたJpaRepositoryFactory

JpaRepositoryFactoryトランザクションを自動的に作成しないことを除けば、実際にはかなりうまく機能します。トランザクションは手動で管理する必要があります。そうしないと、データベースに何も永続化されません。

entityManager.getTransaction().begin();
repositoryInstance.save(someJpaObject);
entityManager.getTransaction().commit();

@Transactional問題は、注釈が自動的に使用されないことであることが判明しました。TransactionInterceptor

ありがたいことに、JpaRepositoryFactoryはコールバックを取得して、返される前に、生成されたリポジトリ プロキシにさらに AOP アドバイスを追加できます。

final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
final JpaRepositoryFactory factory = new JpaRepositoryFactory(emf.createEntityManager());

factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
    @Override
    public void postProcess(ProxyFactory factory) {
        factory.addAdvice(new TransactionInterceptor(xactManager, new AnnotationTransactionAttributeSource()));
    }
});

これは物事がうまくいっていないところです。コードでデバッガーをステップ実行すると、TransactionInterceptor確かにトランザクションが作成されますが、間違っていEntityManagerます。Spring はEntityManager、現在実行中のスレッドを見てアクティブを管理します。はTransactionInterceptorこれを行い、スレッドへのアクティブなEntityManagerバインドがないことを確認し、新しいスレッドを作成することを決定します。

ただし、この newは、作成されてコンストラクターにEntityManager渡されたものと同じインスタンスではなく、. 問題は、どうすれば と を同じように使用できるかということです。JpaRepositoryFactoryEntityManagerTransactionInterceptorJpaRepositoryFactoryEntityManager

アップデート:

これを書きながら、問題を解決する方法を見つけましたが、それでも理想的な解決策ではないかもしれません. この解決策は別の回答として投稿します。私が解決した方法よりも、Spring Data JPA をスタンドアロンで使用するためのより良い方法についての提案をお待ちしております。

4

2 に答える 2

26

JpaRepositoryFactorySpring 統合JpaRepositoryFactoryBeanの設計の背後にある一般原則は次のとおりです。

マネージドJPA ランタイム環境内でアプリケーションを実行することを前提としています。

EntityManagerこれが、ではなく注入に依存する理由EntityManagerFactoryです。定義により、EntityManagerはスレッドセーフではありません。そのため、直接処理する場合はEntityManagerFactory、マネージド ランタイム環境 (Spring や EJB など) が提供するすべてのリソース管理コードを書き直す必要があります。

Spring トランザクション管理と統合するために、SharedEntityManagerCreator手動で実装したトランザクション リソース バインディング マジックを実際に実行する Spring を使用します。EntityManagerしたがって、おそらくそれを使用して、からインスタンスを作成することをお勧めしますEntityManagerFactoryrepo.save(…)リポジトリ Bean で直接トランザクション性をアクティブ化する (まだアクティブでない場合に eg を呼び出すとトランザクションが作成されるようにする) 場合はTransactionalRepositoryProxyPostProcessor、Spring Data Commons の実装を参照してください。Spring Data リポジトリが直接使用される場合 (例: など) に実際にトランザクションをアクティブ化repo.save(…)し、実装クラスよりもインターフェースを優先するようにトランザクション構成ルックアップをわずかにカスタマイズして、リポジトリ インターフェースが で定義されたトランザクション構成をオーバーライドできるようにしますSimpleJpaRepository

于 2012-02-05T12:36:08.377 に答える
14

でリポジトリを作成する前に、実行中のスレッドにEntityManagerandを手動でバインドすることでこれを解決しました。これは、次のメソッドを使用して実現されます。EntityManagerFactoryJpaRepositoryFactoryTransactionSynchronizationManager.bindResource

emf = Persistence.createEntityManagerFactory("com.foo.model", properties);
em = emf.createEntityManager();

// Create your transaction manager and RespositoryFactory
final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
final JpaRepositoryFactory factory = new JpaRepositoryFactory(em);

// Make sure calls to the repository instance are intercepted for annotated transactions
factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
    @Override
    public void postProcess(ProxyFactory factory) {
        factory.addAdvice(new TransactionInterceptor(xactManager, new MatchAlwaysTransactionAttributeSource()));
    }
});

// Create your repository proxy instance
FooRepository repository = factory.getRepository(FooRepository.class);

// Bind the same EntityManger used to create the Repository to the thread
TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));

try{
    repository.save(someInstance); // Done in a transaction using 1 EntityManger
} finally {
    // Make sure to unbind when done with the repository instance
    TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
}

もっと良い方法があるはずです。EnitiyManagerRepositoryFactory がの代わりに使用するように設計されているのは奇妙に思えますEntityManagerFactoryEntityManger最初に anがスレッドにバインドされているかどうかを確認してから、新しいものを作成してバインドするか、既存のものを使用することを期待します。

基本的に、リポジトリ プロキシを挿入し、すべての呼び出しで内部的に新しい を作成することを期待してEntityManager、呼び出しがスレッド セーフになるようにします。

于 2012-02-03T14:41:18.343 に答える