如何在弹簧容器之外使用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理活动的EntityManagerTransactionInterceptor这样做,并看到没有活动的EntityManager绑定到线程,并决定创build一个新的。

但是,这个新的EntityManager不是创build并传递到JpaRepositoryFactory构造函数中的EntityManager ,它需要一个EntityManager 。 问题是,如何使TransactionInterceptorJpaRepositoryFactory使用相同的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。

我通过手动将EntityManagerEntityManagerFactory绑定到正在执行的线程来解决此问题,然后使用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 ,以便调用是线程安全的。