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
がないことを確認し、新しいスレッドを作成することを決定します。
ただし、この新しいEntityManager
は、作成されてJpaRepositoryFactory
コンストラクターに渡されたインスタンスとは異なります。これには、EntityManager
が必要です。問題は、TransactionInterceptor
とJpaRepositoryFactory
で同じEntityManager
を使用するにはどうすればよいですか?
これを書いている間に問題を解決する方法を見つけましたが、それでもまだ理想的な解決策ではないかもしれません。このソリューションを別の回答として投稿します。 Spring Data JPAをスタンドアロンで使用するより良い方法について、私がそれを解決する方法よりも提案があれば喜んで教えてください。
JpaRepositoryFactory
および対応するSpring統合JpaRepositoryFactory
Beanの設計の背後にある一般的な原則は次のとおりです。
managed JPAランタイム環境内でアプリケーションを実行することを想定しています。
これが、EntityManager
ではなく、挿入されたEntityManagerFactory
に依存している理由です。定義により、EntityManager
はスレッドセーフではありません。したがって、EntityManagerFactory
を直接処理する場合、マネージドランタイム環境(SpringまたはEJBと同様)が提供するすべてのリソース管理コードを書き換える必要があります。
Springトランザクション管理と統合するには、SpringのSharedEntityManagerCreator
を使用します。これは、手動で実装したトランザクションリソースバインディングマジックを実際に実行します。そのため、おそらくそのインスタンスを使用して、EntityManager
からEntityManagerFactory
インスタンスを作成する必要があります。リポジトリBeanでトランザクション性を直接アクティブ化する場合(たとえば、repo.save(…)
を呼び出すと、トランザクションがまだアクティブでない場合はトランザクションが作成されます)、Spring Data CommonsのTransactionalRepositoryProxyPostProcessor
実装を確認してください。これは、Spring Dataリポジトリが直接使用されたときにトランザクションを実際にアクティブ化し(repo.save(…)
などの場合)、トランザクション構成ルックアップをわずかにカスタマイズして、リポジトリインターフェースがSimpleJpaRepository
で定義されたトランザクション構成をオーバーライドできるように、実装クラスよりもインターフェースを優先します。
EntityManager
でリポジトリを作成する前に、EntityManagerFactory
とJpaRepositoryFactory
を実行中のスレッドに手動でバインドすることでこれを解決しました。これは、TransactionSynchronizationManager.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());
}
しかし、もっと良い方法があるはずです。 RepositoryFactoryがEnitiyManager
ではなくEntityManagerFactory
を使用するように設計されているのは奇妙に思われます。私は、EntityManger
がスレッドにバインドされているかどうかを最初に確認してから、新しいスレッドを作成してバインドするか、既存のスレッドを使用することを期待しています。
基本的に、私はリポジトリプロキシを挿入し、すべての呼び出しで内部的に新しいEntityManager
を作成して、呼び出しがスレッドセーフになることを期待します。