如何在弹簧容器之外使用Spring Data JPA?
我正在尝试连接Spring Data JPA对象,以便我可以生成DAO代理(又名存储库) – 不使用Spring bean容器。
不可避免的是,我会被问到为什么要这样做:这是因为我们的项目已经在使用Google Guice(并且在使用Gin和GWT的UI上),我们不想维护另一个IoC容器configuration,所有由此产生的依赖关系。 我知道我们可以使用Guice的SpringIntegration
,但这是最后的手段。
看起来,所有东西都可以手动连接对象,但由于没有很好的文档logging,所以我很困难。
根据Spring Data用户指南, 独立使用仓库工厂是可能的。 不幸的是,这个例子显示了RepositoryFactorySupport
是一个抽象类。 经过一番search,我设法findJpaRepositoryFactory
JpaRepositoryFactory
实际上工作得很好,除了它不会自动创build事务。 事务必须手动pipe理,否则什么都不会持久化到数据库:
entityManager.getTransaction().begin(); repositoryInstance.save(someJpaObject); entityManager.getTransaction().commit();
问题原来是@Transactional
注释不会自动使用,并需要一个TransactionInterceptor
值得庆幸的是,在返回之前, JpaRepositoryFactory
可以进行callback以将更多的AOPbuild议添加到生成的Repository代理中:
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())); } });
这是事情做得不好的地方。 通过代码中的debugging器, TransactionInterceptor
确实创build了一个事务 – 但在错误的EntityManager
。 Spring通过查看当前正在执行的线程来pipe理活动的EntityManager
。 TransactionInterceptor
这样做,并看到没有活动的EntityManager
绑定到线程,并决定创build一个新的。
但是,这个新的EntityManager
不是创build并传递到JpaRepositoryFactory
构造函数中的EntityManager
,它需要一个EntityManager
。 问题是,如何使TransactionInterceptor
和JpaRepositoryFactory
使用相同的EntityManager
?
更新:
在写这篇文章的时候,我发现了如何解决这个问题,但它可能不是最理想的解决scheme。 我将发布这个解决scheme作为一个单独的答案。 我会很乐意听到任何build议,以更好的方式来使用Spring Data JPA独立,而不是我如何解决它。
JpaRepositoryFactory
和相应的Spring集成JpaRepositoryFactory
bean的devise背后的一般原则如下:
我们假设您在托pipe的 JPA运行时环境中运行应用程序,而不是关心哪一个。
这就是我们dependency injectionEntityManager
而不是EntityManagerFactory
。 根据定义, EntityManager
不是线程安全的。 所以如果直接处理一个EntityManagerFactory
,我们将不得不重写所有的资源pipe理代码,一个托pipe的运行时环境(就像Spring或者EJB)会提供给你的。
为了与Spring事务pipe理集成,我们使用了Spring的SharedEntityManagerCreator
,它实际上是手动实现的事务资源绑定的魔法。 所以你可能想用它来从你的EntityManagerFactory
创buildEntityManager
实例。 如果要直接激活存储库bean的事务性(如果没有任何活动已经激活,那么对repo.save(…)
的调用将创build一个事务),请参阅Spring Data Commons中的TransactionalRepositoryProxyPostProcessor
实现。 当直接使用Spring Data存储库(例如repo.save(…)
)时,它实际上会激活事务,并稍微定制事务configuration查找,以使接口优先于实现类,以允许存储库接口覆盖SimpleJpaRepository
定义的事务configuration。
我通过手动将EntityManager
和EntityManagerFactory
绑定到正在执行的线程来解决此问题,然后使用JpaRepositoryFactory
创build存储库。 这是使用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被devise为使用EnitiyManager
而不是EntityManagerFactory
,这似乎很奇怪。 我期望,它会首先查看EntityManger
是否绑定到线程,然后创build一个新的绑定它,或使用现有的。
基本上,我想要注入存储库代理,并期望每一个调用他们在内部创build一个新的EntityManager
,以便调用是线程安全的。