【发布时间】:2014-10-30 07:59:50
【问题描述】:
我将我的 Spring 批处理进程从命令行应用程序迁移到 Spring Boot Web 应用程序,包括 spring-batch-admin-manager(版本 1.3.0)。
我的旧命令行应用程序使用两个 JPA 数据库和两个 TransactionManager 实例。我记得要运行它是一个地狱般的配置。从 Spring Boot 开始时,我预计事情会变得更舒服,但现在情况似乎更糟:
Spring 无法为事务选择正确的 TransactionManager 实例。
在应用程序启动时,Spring 正在访问我的一个类以执行带有 @PostConstruct 注释的代码块,应该从中调用事务方法。
@PostConstruct
public void init() {
eventType = eventTypeBo.findByLabel("Sport"); // <-- Calling transactional method
//...
}
从这里抛出错误。看看堆栈跟踪:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined:
expected single matching bean but found 2: transactionManager,osm.transactionManager
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:313)
at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:337)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:252)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy89.findByLabel(Unknown Source)
at com.company.model.service.impl.EventTypeBo.findByLabel(EventTypeBo.java:43)
at com.company.batch.article.utils.converter.SomeConverter.init(SomeConverter.java:83)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//...
从错误日志中可以看出,我的TransactionManagers 被命名为“transactionManager”和“osm.transactionManager”。相应地配置事务:
<!-- DATABASE 1 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- DATABASE 2 -->
<bean id="osm.transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="osm.entityManagerFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="osm.transactionManager">
<tx:attributes>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="osmServiceOperation"
expression="execution(* com.qompa.osm.service.spec..*Service.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="osmServiceOperation" />
</aop:config>
我在访问数据时使用了两种不同的 GenericDao 实现来区分PersistenceContexts:
public class OsmGenericDao<T> implements IGenericDao<T> {
@PersistenceContext(unitName = "osm.entityManagerFactory")
protected EntityManager em;
//...
}
public class GenericDao<T> implements IGenericDao<T> {
@PersistenceContext(unitName = "entityManagerFactory")
protected EntityManager em;
//...
}
一切似乎都已正确配置:但仍然失败!
编辑: 据我所知,Spring 的TransactionAspectSupport 尝试通过限定符 (determineTransactionManager) 确定事务管理器。因为 findByLabel 的 @Transactional 注释没有限定符,它试图在 DefaultListableBeanFactory 的 getBean 方法的帮助下确定正确的 bean,其中发现了两个无法进一步区分的相同类型的 bean。
一定有办法告诉Spring根据持久化上下文选择transactionManager!
有什么想法吗?
【问题讨论】:
-
放弃
<tx:adivce />和<aop:config />并从<tx:annotation-driven />中删除transaction-manager="transactionManager"。而是在任何地方使用@Transactional并指定要在这些@Transactionalbean 上使用哪个事务管理器。 -
我发现这是一个丑陋的解决方案,因为我将我的模块绑定到特定的 TransactionManager 名称。它以前一直在工作,所以现在应该......一定有另一种方法!
-
所以您不介意将其绑定到特定的实体管理器,但 tx 管理器是个问题?使用
@Transactional,您需要指定使用哪个,以防有多个管理器。如果你不使用多个<tx:advice />块。 -
可能我有点挑剔……我试试看!给代码库中的每个 @Transactional 注释添加一个名称对我来说仍然看起来很难看(这意味着重构近一百个类)
-
确实很丑。我认为您的配置一定有问题,因为我记得使用两个单独的持久性上下文没有问题。我会尝试找到它并返回答案。
标签: java spring jpa spring-boot spring-transactions