我正在尝试手动连接Spring Data JPA对象,以便我可以生成DAO代理(也称为存储库) - 而无需使用Spring bean容器。
不可避免地,我会被问到为什么要这样做:这是因为我们的项目已经在使用Google Guice(以及在使用Gin和GWT的UI上),而且我们不想维护另一个IoC容器配置,或者拉入所有产生的依赖关系。我知道我们可以使用Guice的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
绑定到该线程,并决定创建一个新的。{/ p>
但是,这个新的EntityManager
与创建并传递到JpaRepositoryFactory
构造函数的实例不同,后者需要EntityManager
。问题是,如何使TransactionInterceptor
和JpaRepositoryFactory
使用相同的EntityManager
?
在写这篇文章时,我发现了如何解决问题,但它仍然可能不是理想的解决方案。我将此解决方案作为单独的答案发布。我很高兴听到有关更好地使用Spring Data JPA独立方法的建议,而不是我如何解决它。
答案 0 :(得分:24)
设计JpaRepositoryFactory
和相应的Spring集成JpaRepositoryFactory
bean背后的一般原则如下:
我们假设您在托管的 JPA运行时环境中运行您的应用程序,而不是关心哪一个。
这就是我们依赖注入EntityManager
而不是EntityManagerFactory
的原因。根据定义,EntityManager
不是线程安全的。因此,如果直接处理EntityManagerFactory
,我们将不得不重写所有资源管理代码,托管运行时环境(就像Spring或EJB)将为您提供。
为了与Spring事务管理集成,我们使用Spring SharedEntityManagerCreator
来实际执行您手动实现的事务资源绑定魔法。因此,您可能希望使用该实例从EntityManager
创建EntityManagerFactory
个实例。如果要直接激活存储库bean中的事务性(以便调用例如repo.save(…)
创建事务,如果没有活动),请查看Spring Data Commons中的TransactionalRepositoryProxyPostProcessor
实现。它实际上在直接使用Spring Data存储库时激活事务(例如,对于repo.save(…)
),并稍微自定义事务配置查找以优先于实现类的接口,以允许存储库接口覆盖SimpleJpaRepository
中定义的事务配置。 / p>
答案 1 :(得分:12)
我在创建具有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
是否绑定到该线程,然后创建一个新的并绑定它,或使用现有的。{/ p>
基本上,我想注入存储库代理,并期望在每次调用时他们在内部创建一个新的EntityManager
,以便调用是线程安全的。