我正在尝试手动连接Spring Data JPA对象,以便我可以生成DAO代理(也称为存储库) - 而无需使用Spring bean容器 .
不可避免地,我会被问到为什么要这样做:这是因为我们的项目已经在使用Google Guice(并且在使用Gin和GWT的UI上),我们不会't want to maintain another IoC container configuration, or pull in all the resulting dependencies. I know we might be able to use Guice' s SpringIntegration
,但这将是最后的选择 .
似乎所有东西都可用于手动连接对象,但由于它没有很好地记录,我现在遇到了困难 .
根据Spring Data用户指南,可以使用repository factories standalone . 不幸的是,该示例显示 RepositoryFactorySupport
这是一个抽象类 . 经过一番搜索,我找到了JpaRepositoryFactory
JpaRepositoryFactory
实际上工作得很好,除了它不会自动创建交易 . 必须手动管理事务,否则任何内容都不会持久保存到数据库:
entityManager.getTransaction().begin();
repositoryInstance.save(someJpaObject);
entityManager.getTransaction().commit();
问题原来是 @Transactional
注释没有自动使用,需要 TransactionInterceptor
的帮助
值得庆幸的是, JpaRepositoryFactory
可以在返回之前接受回调以向生成的Repository代理添加更多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独立的更好方法的建议,而不是我如何解决它 .
2 回答
设计
JpaRepositoryFactory
和相应的Spring集成JpaRepositoryFactory
bean背后的一般原则如下:这就是我们依赖注入
EntityManager
而不是EntityManagerFactory
的原因 . 根据定义,EntityManager
不是线程安全的 . 因此,如果直接处理EntityManagerFactory
,我们将不得不重写所有资源管理代码,托管运行时环境(就像Spring或EJB)将为您提供 .为了与Spring事务管理集成,我们使用Spring的
SharedEntityManagerCreator
,它实际上是你手动实现的事务资源绑定魔法 . 所以你可能想用那个从你的EntityManagerFactory
创建EntityManager
实例 . 如果要直接激活存储库bean中的事务性(以便调用例如repo.save(…)
,如果没有活动则创建事务),请查看Spring Data Commons中的TransactionalRepositoryProxyPostProcessor
实现 . 它实际上在直接使用Spring Data存储库时激活事务(例如repo.save(…)
),并稍微自定义事务配置查找以优先于实现类的接口,以允许存储库接口覆盖SimpleJpaRepository
中定义的事务配置 .我在使用
JpaRepositoryFactory
创建存储库之前,通过手动将EntityManager
和EntityManagerFactory
绑定到正在执行的线程来解决这个问题 . 这是使用TransactionSynchronizationManager.bindResource
方法完成的:必须有更好的方法 . 看起来奇怪的是RepositoryFactory被设计为使用
EnitiyManager
而不是EntityManagerFactory
. 我希望,它首先会查看EntityManger
是否绑定到该线程,然后创建一个新的并绑定它,或使用现有的 .基本上,我想注入存储库代理,并期望在每次调用时它们在内部创建一个新的
EntityManager
,以便调用是线程安全的 .