49

我正在尝试手动连接 Spring Data JPA 对象,以便我可以生成 DAO 代理(又名存储库)——而不使用 Spring bean 容器。

不可避免地,有人会问我为什么要这样做:因为我们的项目已经在使用 Google Guice(并且在 UI 上使用带有 GWT 的 Gin),并且我们不想维护另一个 IoC 容器配置,或者引入所有由此产生的依赖关系。我知道我们也许可以使用 Guice's SpringIntegration,但这将是最后的手段。

似乎一切都可以手动连接对象,但由于没有很好的记录,我遇到了困难。

根据 Spring Data 用户指南,可以独立使用存储库工厂。不幸的是,该示例显示了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. SpringEntityManager通过查看当前正在执行的线程来管理活动。执行TransactionInterceptor此操作并看到线程没有活动EntityManager绑定,并决定创建一个新绑定。

但是,这个 newEntityManager与创建并传递给JpaRepositoryFactory构造函数的实例不同,它需要一个EntityManager. 问题是,我如何使TransactionInterceptorJpaRepositoryFactory使用相同EntityManager

更新:

在写这篇文章时,我发现了如何解决这个问题,但它仍然可能不是理想的解决方案。我将将此解决方案作为单独的答案发布。我很高兴听到任何关于独立使用 Spring Data JPA 的更好方法的建议,而不是我解决它的方法。

4

2 回答 2

26

JpaRepositoryFactory设计和相应的 Spring 集成bean背后的一般原则JpaRepositoryFactory如下:

我们假设您在托管的JPA 运行时环境中运行您的应用程序,而不关心是哪一个。

这就是我们依赖注入EntityManager而不是EntityManagerFactory. 根据定义,EntityManager它不是线程安全的。因此,如果直接处理,EntityManagerFactory我们将不得不重写托管运行时环境(就像 Spring 或 EJB)将为您提供的所有资源管理代码。

为了与 Spring 事务管理集成,我们使用 Spring SharedEntityManagerCreator,它实际上执行了您手动实现的事务资源绑定魔术。所以你可能想用那个EntityManager从你的EntityManagerFactory. 如果您想直接激活存储库 bean 的事务性(以便调用 egrepo.save(…)创建一个事务,如果没有一个事务已经处于活动状态),请查看TransactionalRepositoryProxyPostProcessorSpring Data Commons 中的实现。当直接使用 Spring Data 存储库时,它实际上会激活事务(例如 for repo.save(…)),并稍微自定义事务配置查找以优先于实现类的接口,以允许存储库接口覆盖定义的事务配置SimpleJpaRepository

于 2012-02-05T12:36:08.377 回答
14

EntityManager使用. _ 这是使用以下方法完成的:EntityManagerFactoryJpaRepositoryFactoryTransactionSynchronizationManager.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. 我希望,它会首先查看是否将 anEntityManger绑定到线程,然后创建一个新的并绑定它,或者使用现有的。

基本上,我想注入存储库代理,并期望他们在每次调用时都会在内部创建一个 new EntityManager,以便调用是线程安全的。

于 2012-02-03T14:41:18.343 回答