问题描述
我正在尝试手动连接 Spring Data JPA 对象,以便我可以生成 DAO 代理(又名存储库) - 不使用 Spring bean 容器.
I'm trying to wire up Spring Data JPA objects manually so that I can generate DAO proxies (aka Repositories) - without using a Spring bean container.
不可避免地,我会被问到为什么要这样做:因为我们的项目已经在使用 Google Guice(并且在 UI 上使用 Gin 和 GWT),并且我们不想维护另一个 IoC 容器配置,或拉入所有产生的依赖项.我知道我们也许可以使用 Guice 的 SpringIntegration
,但这将是最后的手段.
Inevitably, I will be asked why I want to do this: it is because our project is already using Google Guice (and on the UI using Gin with GWT), and we don'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
, but this would be a last resort.
似乎所有东西都可以手动连接对象,但由于没有很好的记录,我遇到了困难.
It seems that everything is available to wire the objects up manually, but since it's not well documented, I'm having a difficult time.
根据 Spring Data 用户指南,使用 独立的存储库工厂 是可能的.不幸的是,该示例显示了 RepositoryFactorySupport
,它是一个抽象类.经过一番搜索,我设法找到了 JpaRepositoryFactory
According to the Spring Data user's guide, using repository factories standalone is possible. Unfortunately, the example shows RepositoryFactorySupport
which is an abstract class. After some searching I managed to find JpaRepositoryFactory
JpaRepositoryFactory
实际上工作得相当好,只是它不会自动创建事务.事务必须手动管理,否则什么都不会持久化到数据库中:
JpaRepositoryFactory
actually works fairly well, except it does not automatically create transactions. Transactions must be managed manually, or nothing will get persisted to the database:
entityManager.getTransaction().begin();
repositoryInstance.save(someJpaObject);
entityManager.getTransaction().commit();
问题原来是@Transactional
注解没有自动使用,需要TransactionInterceptor
The problem turned out to be that @Transactional
annotations are not used automatically, and need the help of a TransactionInterceptor
值得庆幸的是,JpaRepositoryFactory
可以在返回之前通过回调向生成的 Repository 代理添加更多 AOP 建议:
Thankfully, the JpaRepositoryFactory
can take a callback to add more AOP advice to the generated Repository proxy before returning:
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
绑定到线程,并决定创建一个新的.
This is where things are not working out so well. Stepping through the debugger in the code, the TransactionInterceptor
is indeed creating a transaction - but on the wrong EntityManager
. Spring manages the active EntityManager
by looking at the currently executing thread. The TransactionInterceptor
does this and sees there is no active EntityManager
bound to the thread, and decides to create a new one.
然而,这个新的 EntityManager
与创建并传递到 JpaRepositoryFactory
构造函数的实例不同,后者需要一个 EntityManager
.问题是,如何让 TransactionInterceptor
和 JpaRepositoryFactory
使用相同的 EntityManager
?
However, this new EntityManager
is not the same instance that was created and passed into the JpaRepositoryFactory
constructor, which requires an EntityManager
. The question is, how do I make the TransactionInterceptor
and the JpaRepositoryFactory
use the same EntityManager
?
在写这篇文章的时候,我发现了如何解决这个问题,但它仍然可能不是理想的解决方案.我会将此解决方案作为单独的答案发布.我很高兴听到任何关于独立使用 Spring Data JPA 的更好方法的建议,而不是我的解决方法.
While writing this up, I found out how to solve the problem but it still may not be the ideal solution. I will post this solution as a separate answer. I would be happy to hear any suggestions on a better way to use Spring Data JPA standalone than how I've solve it.
推荐答案
JpaRepositoryFactory
和相应的 Spring 集成 JpaRepositoryFactory
bean 设计背后的一般原则如下:
The general principle behind the design of JpaRepositoryFactory
and the according Spring integration JpaRepositoryFactory
bean is the following:
我们假设您在托管 JPA 运行时环境中运行您的应用程序,而不关心是哪一个.
这就是我们依赖注入的 EntityManager
而不是 EntityManagerFactory
的原因.根据定义,EntityManager
不是线程安全的.因此,如果直接处理 EntityManagerFactory
,我们将不得不重写托管运行时环境(就像 Spring 或 EJB)将为您提供的所有资源管理代码.
That's the reason we rely on injected EntityManager
rather than an EntityManagerFactory
. By definition the EntityManager
is not thread safe. So if dealt with an EntityManagerFactory
directly we would have to rewrite all the resource managing code a managed runtime environment (just like Spring or EJB) would provide you.
为了与 Spring 事务管理集成,我们使用 Spring 的 SharedEntityManagerCreator
实际上执行您手动实现的事务资源绑定魔术.所以你可能想用那个从你的 EntityManagerFactory
创建 EntityManager
实例.如果您想直接在存储库 bean 中激活事务性(以便调用例如 repo.save(…)
在没有活动的情况下创建一个事务),请查看 TransactionalRepositoryProxyPostProcessor
在 Spring Data Commons 中的实现.当直接使用 Spring Data 存储库时(例如,对于 repo.save(…)
),它实际上会激活事务,并稍微自定义事务配置查找以优先于实现类的接口,以允许存储库接口覆盖定义的事务配置在 SimpleJpaRepository
中.
To integrate with the Spring transaction management we use Spring's SharedEntityManagerCreator
that actually does the transaction resource binding magic you've implemented manually. So you probably want to use that one to create EntityManager
instances from your EntityManagerFactory
. If you want to activate the transactionality at the repository beans directly (so that a call to e.g. repo.save(…)
creates a transaction if none is already active) have a look at the TransactionalRepositoryProxyPostProcessor
implementation in Spring Data Commons. It actually activates transactions when Spring Data repositories are used directly (e.g. for repo.save(…)
) and slightly customizes the transaction configuration lookup to prefer interfaces over implementation classes to allow repository interfaces to override transaction configuration defined in SimpleJpaRepository
.
这篇关于您如何在 Spring Container 之外使用 Spring Data JPA?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!