我正在更新现有的Java SE Camel应用程序,以通过@PersitentContext注入来利用Spring JPA管理的EntityManger。当前,在我们的代码中使用Persistence.createEntityManagerFactory实例化了它,并且效果很好。但是,我们希望通过注入该entityManager来拥有更灵活和可测试的应用程序,使用强大的@Transactional aop功能,还可以通过使用不同的@Configuration @Profiles在本地和全局XA事务之间进行切换。
我的第一个测试是使用Atomikos TransactionManager测试涉及JMS和JPA资源的xa事务,并且可以正常工作。
现在在此应用程序中,我想使用OpenJPA,Camel和JpaTransactionManager测试本地事务,即不涉及XA事务。
我的单元测试在没有Camel的情况下效果很好,@ Transactional编织完成了使用jpaTransactionManager启动事务的工作,一切都很好。
现在,当我尝试将配置Bean集成到骆驼应用程序中时,一切进展不顺利。
我有一个JMS使用者,该使用者触发一个骆驼处理器,其中的处理方法用@Transactional注释。
我认为我配置有误,因为JMSConsumer很难启动事务,因为我没有明确设置JMS组件为事务性的(路由中没有transacted(),没有将事务管理器设置为JMSConfiguration)
这是一个断点,显示了JmsConsumer尝试注册到事务同步内容:
Daemon Thread [Camel (worker) thread #1 - JmsConsumer[<consumerName>]] (Suspended (breakpoint at line 175 in TransactionSynchronizationManager))
TransactionSynchronizationManager.bindResource(Object, Object) line: 175
DefaultJmsMessageListenerContainer(AbstractPollingMessageListenerContainer).doReceiveAndExecute(Object, Session, MessageConsumer, TransactionStatus) line: 313
DefaultJmsMessageListenerContainer(AbstractPollingMessageListenerContainer).receiveAndExecute(Object, Session, MessageConsumer) line: 255
DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener() line: 1168
DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop() line: 1160
DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run() line: 1057
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1142
ThreadPoolExecutor$Worker.run() line: 617
Thread.run() line: 745 [local variables unavailable]
我看不到任何其他资源绑定到TransactionSynchronisationManager。
相反,在我的单元测试中似乎进展顺利,我们在堆栈中看到@Transactional发挥了预期的作用:
Thread [main] (Suspended (breakpoint at line 175 in TransactionSynchronizationManager))
TransactionSynchronizationManager.bindResource(Object, Object) line: 175
JpaTransactionManager.doBegin(Object, TransactionDefinition) line: 406
JpaTransactionManager(AbstractPlatformTransactionManager).getTransaction(TransactionDefinition) line: 377
TransactionInterceptor(TransactionAspectSupport).createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String) line: 461
TransactionInterceptor(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 277
TransactionInterceptor.invoke(MethodInvocation) line: 96
CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 179
CglibAopProxy$DynamicAdvisedInterceptor.intercept(Object, Method, Object[], MethodProxy) line: 671
MyRepo$$EnhancerBySpringCGLIB$$5edadd7f.createRun(String) line: not available
DataSourceConfigTest.testIt2() line: 38
...
所以显然这里有些配置错误,似乎@Transactional被SpringBoot的一些神奇的自动配置完全忽略或覆盖了。有人有任何提示吗?
这是我的CamelConfiguration和My Data / JPA配置
骆驼配置
@Configuration
public class CamelConfiguration {
protected static Logger LOGGER = LoggerFactory.getLogger(CamelConfiguration.class);
@Value("${broker.mqURL}")
String mqURL;
/**
* The runId is injected from command Line arguments
*/
@Value("${runId}")
long runId;
@Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
@Override
public void beforeApplicationStart(CamelContext context) {
((SpringCamelContext)context).setName("worker");
DefaultShutdownStrategy shutdownStrategy = new DefaultShutdownStrategy();
shutdownStrategy.setTimeUnit(TimeUnit.SECONDS);
shutdownStrategy.setTimeout(5);
context.setShutdownStrategy(shutdownStrategy);
}
@Override
public void afterApplicationStart(CamelContext camelContext) {
try {
LOGGER.info("Starting route dataspecProcessing." + runId);
camelContext.startAllRoutes();
} catch (Exception e) {
LOGGER.error("Error during Camel post start",e);
}
}
};
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public ActiveMQConnectionFactory jmsConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(mqURL);
connectionFactory.setTrustAllPackages(true);
return connectionFactory;
}
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public JmsConfiguration myJmsConfiguration(ActiveMQConnectionFactory jmsConnectionFactory,JpaTransactionManager jpaTransactionManager) {
JmsConfiguration jmsConfiguration = new JmsConfiguration();
// jmsConfiguration.setTransacted(true);
// jmsConfiguration.setLazyCreateTransactionManager(false);
// jmsConfiguration.setTransactionManager(jpaTransactionManager);
jmsConfiguration.setConnectionFactory(jmsConnectionFactory);
jmsConfiguration.setCacheLevelName("CACHE_CONSUMER");
return jmsConfiguration;
}
Jpa配置
@Configuration
@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DataSourceConfig {
@Bean
public JpaTransactionManager jpaTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
transactionManager.setDataSource(dataSource());
transactionManager.setJpaDialect(new OpenJpaDialect());
return transactionManager;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceXmlLocation("META-INF/persistence.xml");
em.setJpaProperties(additionalProperties());
em.setDataSource(dataSource());
em.setValidationMode(ValidationMode.NONE);
JpaVendorAdapter vendorAdapter = new OpenJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@Bean
public DataSource dataSource() {
PGSimpleDataSource pgDataSource = new PGSimpleDataSource();
pgDataSource.setUrl(...);
pgDataSource.setUser(...);
pgDataSource.setPassword(...);
return pgDataSource;
}
应该是可交易的代码
@Component
@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class MyCamelProcessor implements Processor {
...
@Transactional(propagation=Propagation.REQUIRES_NEW,transactionManager="jpaTransactionManager")
public void process(Exchange exchange) throws Exception {
... calls to entitymanager here ...
调用处理器的路由
@Component
public class DataSpecProcessingRoute extends SpringRouteBuilder {
....
public void configure() throws Exception {
...
from("jms:queue:thequeue"?concurrentConsumers=" + numConsumerThreads)
.autoStartup(false)
.shutdownRunningTask(ShutdownRunningTask.CompleteCurrentTaskOnly)
.routeId("dataspecProcessing." + runId)
.process(initialisationProcessor)
.process(processorWithTransactionnalNeeded)
.to("jms:queue:anotherqueue") ;
编辑
它适用于程序化事务,不适用于@Transactional注释。