本文描述spring boot基於Atomikos+DruidXADataSource分佈式事務配置(100%純動態),也就是增長、減小數據源只須要修改application.properties文件,無需動態增長或減小Bean。php
有時候咱們一個應用會有N份部署,每一個須要訪問多個數據源,A環境可能只須要2個數據源,B環境須要5個數據源(由於咱們是行業軟件,因此會有這個狀況,對於純項目的系統,一般沒有這個問題),因此咱們但願代碼只有一份,配置按需調整就肯定了具體的數據源。java
MapperConfig配置:mysql
package com.hundsun.ta.aop.config; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @Configuration public class MybatisConfig { @Order(1) @Bean public MapperScannerConfigurer mapperScannerConfigurer1() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "default"); mapperScannerConfigurer.setBasePackage("com.hundsun.ta.base.mapper;com.hundsun.ta.aop.sysinfo.mapper;com.hundsun.ta.aop.parameters.mapper;com.hundsun.ta.aop.interfile.mapper;com.hundsun.ta.aop.demo.mapper;com.hundsun.ta.aop.config.mapper;com.hundsun.ta.aop.base.mapper;com.hundsun.ta.aop.auditresult.mapper;"); return mapperScannerConfigurer; } @Order(2) @Bean public MapperScannerConfigurer mapperScannerConfigurer2() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga1"); mapperScannerConfigurer.setBasePackage("com.hundsun.ta.aop.ta.mapper;com.hundsun.ta.aop.ta.*.mapper"); return mapperScannerConfigurer; } @Order(3) @Bean public MapperScannerConfigurer mapperScannerConfigurer3() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga2"); mapperScannerConfigurer.setBasePackage("com.hundsun.ta.aop.yoga.*.mapper;com.hundsun.ta.aop.ta4.**.mapper"); return mapperScannerConfigurer; } }
XA配置web
package com.hundsun.ta.datasource; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; @ConfigurationProperties(prefix="dyn.spring") @PropertySource("classpath:jrescloud.properties") public class DynamicDataSourceConfig { private List<DataSource> datasources; public static class DataSource { private String name; private String driverClassName; private String url; private String username; private String password; private int maxActive; private int maxIdle; private String mapperLocations; private String basePackage; public int getMaxActive() { return maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public int getMaxIdle() { return maxIdle; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public int getMaxWait() { return maxWait; } public void setMaxWait(int maxWait) { this.maxWait = maxWait; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public boolean isDefaultAutoCommit() { return defaultAutoCommit; } public void setDefaultAutoCommit(boolean defaultAutoCommit) { this.defaultAutoCommit = defaultAutoCommit; } public String getConnectionInitSqls() { return connectionInitSqls; } public void setConnectionInitSqls(String connectionInitSqls) { this.connectionInitSqls = connectionInitSqls; } private int maxWait; private String validationQuery; private boolean defaultAutoCommit; private String connectionInitSqls; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getMapperLocations() { return mapperLocations; } public void setMapperLocations(String mapperLocations) { this.mapperLocations = mapperLocations; } public String getBasePackage() { return basePackage; } public void setBasePackage(String basePackage) { this.basePackage = basePackage; } } public List<DataSource> getDatasources() { return datasources; } public void setDatasources(List<DataSource> datasources) { this.datasources = datasources; } }
package com.hundsun.ta.datasource; import java.util.Properties; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Service; import com.alibaba.druid.pool.xa.DruidXADataSource; import com.atomikos.jdbc.AtomikosDataSourceBean; import com.hundsun.ta.datasource.DynamicDataSourceConfig.DataSource; import oracle.jdbc.xa.client.OracleXADataSource; @Service public class DynamicDataSourceRegister implements InitializingBean,ApplicationContextAware,BeanPostProcessor { @Value("${dbType}") private String dbType; @Value("${mybatis.mapperLocations}") private String mapperLocations; @Value("${mybatis.configLocation}") private String configLocation; @Value("${mybatis.typeAliasesPackage}") private String typeAliasesPackage; private ApplicationContext applicationContext; @Autowired private DynamicDataSourceConfig config; @Override public void afterPropertiesSet() throws Exception { // Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext; // 獲取bean工廠並轉換爲DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory(); for(DataSource datasource : config.getDatasources()) { RootBeanDefinition rbd = new RootBeanDefinition(AtomikosDataSourceBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true); rbd.setInitMethodName("init"); rbd.setDestroyMethodName("close"); // MutablePropertyValues propertyValues = new MutablePropertyValues(); /*propertyValues.add("url", datasource.getUrl()); // propertyValues.add("url", "jdbc:mysql://" + app.getHostname() + ":" + app.getMapPort() + "/performance_schema?useUnicode=true&characterEncoding=gbk&autoReconnect=true&failOverReadOnly=false"); // propertyValues.add("driverClassName", datasource.getDriverClassName()); propertyValues.add("username", datasource.getUsername()); propertyValues.add("password", datasource.getPassword());*/ // propertyValues.add("password", Base64Util.getFromBase64(datasource.getPassword())); propertyValues.add("minPoolSize", 1); propertyValues.add("maxPoolSize", datasource.getMaxActive()); propertyValues.add("borrowConnectionTimeout", datasource.getMaxWait()); // propertyValues.add("maintenanceInterval", 30); propertyValues.add("xaDataSourceClassName", OracleXADataSource.class.getCanonicalName()); propertyValues.add("uniqueResourceName","xa-" + datasource.getName()); Properties xaProperties = new Properties(); xaProperties.setProperty("URL", datasource.getUrl()); xaProperties.setProperty("user", datasource.getUsername()); xaProperties.setProperty("password", datasource.getPassword()); // xaProperties.setProperty("testOnborrow", "true"); propertyValues.add("xaProperties", xaProperties); rbd.setPropertyValues(propertyValues); rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); beanFactory.registerBeanDefinition("xa-" + datasource.getName(), rbd); // targetDataSources.put(datasource.getName(), applicationContext.getBean(datasource.getName())); rbd = new RootBeanDefinition(SqlSessionFactoryBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true); propertyValues = new MutablePropertyValues(); propertyValues.add("configLocation", configLocation); propertyValues.add("mapperLocations", datasource.getMapperLocations()); propertyValues.add("dataSource", applicationContext.getBean("xa-" + datasource.getName())); rbd.setPropertyValues(propertyValues); rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); beanFactory.registerBeanDefinition("sqlSessionFactory" + datasource.getName(), rbd); // MapperScannerConfigurer本應該也是動態,可是死活報Mapper無實現,因此還在bean中,這是不夠動態的。 /* rbd = new RootBeanDefinition(MapperScannerConfigurer.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true); propertyValues = new MutablePropertyValues(); propertyValues.add("sqlSessionFactoryBeanName", "sqlSessionFactory" + datasource.getName()); propertyValues.add("basePackage", datasource.getBasePackage()); rbd.setPropertyValues(propertyValues); rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); beanFactory.registerBeanDefinition("mapperScanner-" + datasource.getName(), rbd);*/ propertyValues = new MutablePropertyValues(); ConstructorArgumentValues cargs = new ConstructorArgumentValues(); cargs.addIndexedArgumentValue(0, applicationContext.getBean("sqlSessionFactory" + datasource.getName())); rbd = new RootBeanDefinition(SqlSessionTemplate.class, cargs, propertyValues); rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); beanFactory.registerBeanDefinition("sqlSessionTemplate" + datasource.getName(), rbd); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
配置文件:spring
dyn.spring.datasources[0].name=default dyn.spring.datasources[0].driverClassName=oracle.jdbc.OracleDriver dyn.spring.datasources[0].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g dyn.spring.datasources[0].username=hs_aop dyn.spring.datasources[0].password=hs_aop dyn.spring.datasources[0].maxActive=100 dyn.spring.datasources[0].maxWait=5000 dyn.spring.datasources[0].maxIdle=10 dyn.spring.datasources[0].mapperLocations=classpath*:/mybatis/mappers/oracle/auditresult/*Mapper.xml dyn.spring.datasources[0].basePackage=com.hundsun.ta.base.mapper;com.hundsun.ta.aop.sysinfo.mapper;com.hundsun.ta.aop.parameters.mapper;com.hundsun.ta.aop.interfile.mapper;com.hundsun.ta.aop.demo.mapper;com.hundsun.ta.aop.config.mapper;com.hundsun.ta.aop.base.mapper;com.hundsun.ta.aop.auditresult.mapper; # 瑜伽TA分庫 dyn.spring.datasources[1].name=yoga1 dyn.spring.datasources[1].driverClassName=oracle.jdbc.OracleDriver dyn.spring.datasources[1].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g dyn.spring.datasources[1].username=hs_aop dyn.spring.datasources[1].password=hs_aop dyn.spring.datasources[1].maxActive=100 dyn.spring.datasources[1].maxWait=5000 dyn.spring.datasources[1].maxIdle=10 dyn.spring.datasources[1].mapperLocations=classpath*:/mybatis/mappers/oracle/interfile/*Mapper.xml dyn.spring.datasources[1].basePackage=com.hundsun.ta.aop.ta.mapper; dyn.spring.datasources[2].name=yoga2 dyn.spring.datasources[2].driverClassName=oracle.jdbc.OracleDriver dyn.spring.datasources[2].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g dyn.spring.datasources[2].username=yoga2 dyn.spring.datasources[2].password=yoga2 dyn.spring.datasources[2].maxActive=100 dyn.spring.datasources[2].maxWait=5000 dyn.spring.datasources[2].maxIdle=10 dyn.spring.datasources[2].mapperLocations=classpath*:/mybatis/mappers/yoga2/**/*Mapper.xml dyn.spring.datasources[2].basePackage=com.hundsun.ta.aop.yuga.mapper;
這樣就支持分佈式事務了,示例以下:sql
@Transactional @Override public ResultModel<?> insert() { AgencyInfo agencyInfo = new AgencyInfo(); agencyInfo.setAgencyName("4"); agencyInfo.setAgencyNo("4"); agencyInfo.setAgencyStatus("4"); agencyInfo.setSysType("4"); defaultDsMapper.insert(agencyInfo); SubDbInfo subDbInfo = new SubDbInfo(); subDbInfo.setSubDbDataSource("4"); subDbInfo.setSubDbNo("4"); subDbInfo.setSysType("4"); // 非獨立事務 taDsMapper.insert(subDbInfo); List insertList = new ArrayList(); insertList.add("aaa");
//數據源使用SqlSessionTemplate動態切換 baseBatchMapper.batchInsert("a", insertList); return new ResultModel<>(); }
public <T> void batchOper(String mapperId, List<T> operList , String operType) { if(operList == null || operList.isEmpty()) { logger.info("無須要批量入庫的記錄!"); return; }
// 動態傳入數據源便可 sqlSessionTemplate = SpringContextHolder.getBean("sqlSessionTemplate" + "default",SqlSessionTemplate.class); SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
XA存在的一個問題是事務傳播級別REQUIRE_NEW不生效(還沒找到怎麼解決),以下:tomcat
/** * 暫時不支持自治事務 */ @Transactional @Override public ResultModel<?> insertAuto() { AgencyInfo agencyInfo = new AgencyInfo(); agencyInfo.setAgencyName("2"); agencyInfo.setAgencyNo("2"); agencyInfo.setAgencyStatus("2"); agencyInfo.setSysType("2"); defaultDsMapper.insert(agencyInfo); SubDbInfo subDbInfo = new SubDbInfo(); /* subDbInfo.setSubDbDataSource("2"); subDbInfo.setSubDbNo("2"); subDbInfo.setSysType("2");*/ //獨立事務,會報錯,可是整個回滾了 service.insertNew(subDbInfo); agencyInfo.setAgencyName("3"); agencyInfo.setAgencyNo("3"); agencyInfo.setAgencyStatus("3"); agencyInfo.setSysType("3"); defaultDsMapper.insert(agencyInfo); return new ResultModel<>(); }
-- 加上rollbackFor,或者拋出RuntimeException都不行,整個XA被回滾了 @Transactional(propagation=Propagation.REQUIRES_NEW) public ResultModel<?> insertNew(SubDbInfo subDbInfo) { taDsMapper.insert(subDbInfo); return new ResultModel<>(); }
錯誤棧以下:session
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled. [] 2019-02-24 12:05:25 [127362] [o.s.b.SpringApplication]-[ERROR] main Application startup failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'checkItemController' defined in file [E:\恆生TA\TA-BASE\trunk\Sources\stage-source\tajres3.0-demo\tajres3.0-demo-web\target\classes\com\hundsun\ta\aop\demo\controller\CheckItemController.class]: Invocation of init method failed; nested exception is java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1026) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) at com.hundsun.ta.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insertAuto(<generated>) at com.alibaba.dubbo.common.bytecode.Wrapper6.invokeMethod(Wrapper6.java) at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46) at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72) at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:65) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.ta.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.ta.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113) at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84) at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170) at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52) at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: javax.transaction.RollbackException: Prepare: NO vote at com.atomikos.icatch.jta.TransactionImp.rethrowAsJtaRollbackException(TransactionImp.java:66) at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:206) at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:436) at com.atomikos.icatch.jta.UserTransactionManager.commit(UserTransactionManager.java:177) at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1023) ... 44 more Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote at com.atomikos.icatch.imp.ActiveStateHandler.prepare(ActiveStateHandler.java:231) at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:681) at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:970) at com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:82) at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:336) at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:190) ... 47 more at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE] at com.hundsun.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:376) [jrescloud-common-1.0.12.jar:1.0.12] at com.hundsun.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:357) [jrescloud-common-1.0.12.jar:1.0.12] at com.hundsun.ta.aop.ConsumerStarter.main(ConsumerStarter.java:9) [classes/:?] Caused by: java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1026) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) at com.hundsun.ta.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insertAuto(<generated>) at com.alibaba.dubbo.common.bytecode.Wrapper6.invokeMethod(Wrapper6.java) at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46) at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72) at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:65) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.ta.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.ta.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.hundsun.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113) at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84) at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170) at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52) at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: javax.transaction.RollbackException: Prepare: NO vote at com.atomikos.icatch.jta.TransactionImp.rethrowAsJtaRollbackException(TransactionImp.java:66) at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:206) at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:436) at com.atomikos.icatch.jta.UserTransactionManager.commit(UserTransactionManager.java:177) at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1023) ... 44 more Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote at com.atomikos.icatch.imp.ActiveStateHandler.prepare(ActiveStateHandler.java:231) at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:681) at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:970) at com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:82) at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:336) at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:190) ... 47 more at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:109) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.hundsun.ta.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61) ~[classes/:?] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.hundsun.ta.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58) ~[classes/:?] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.hundsun.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68) ~[jrescloud-dubbo-monitor-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.hundsun.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104) ~[jrescloud-dubbo-extend-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_171] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_171] at java.lang.Thread.run(Thread.java:748) ~[?:1.8.0_171]
使用druid xa數據源有一個問題,jstack會看到在獲取鏈接的地方一直WAITING:mybatis
"http-nio-8080-exec-54" daemon prio=10 tid=0x0000000000e61000 nid=0xcc9 waiting on condition [0x00007f4a753d4000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007a143f230> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043) at com.alibaba.druid.pool.DruidDataSource.takeLast(DruidDataSource.java:1732) at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1330) at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1198) at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4619)
換成OracleXA就沒有問題,使用的druid是1.1.10,因此應該不是早期版本bug的問題。oracle
package com.hundsun.ta.aop; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.hundsun.jrescloud.common.annotation.CloudApplication; import com.hundsun.jrescloud.common.boot.CloudBootstrap; @EnableTransactionManagement @CloudApplication(exclude= {DataSourceAutoConfiguration.class,XADataSourceAutoConfiguration.class}) @ComponentScan("com.hundsun.ta.aop") @EnableAspectJAutoProxy(exposeProxy=true) public class ProviderStarter { public static void main(String[] args) { CloudBootstrap.run(ProviderStarter.class, args); } }
atomikos以及spring boot下的幾個陷阱:
atomikos幾個坑:
jta.properties:
com.atomikos.icatch.output_dir=/datayes/atomikos
com.atomikos.icatch.log_base_dir=/datayes/atomikos
若一個tomcat上有兩個atomikos應用,則兩個應用不要公用同一位置,不然會報已經有一個應用。
在IDEA中,若是一個parent下有兩個應用,默認狀況下它們的transaction_log都在parent目錄下,而不是具體應用下,會報上面的這個錯。
mysql XA bug:
Some users have reported problems with MySQL XA (related to this MySQL bug: http://bugs.mysql.com/bug.php?id=27832external). This problem only happens if you access the same MySQL database more than once in the same transaction. A workaround can be setting the following property in classpath:jta.properties:
com.atomikos.icatch.serial_jta_transactions=false
Also, make sure to set the following property on the MySQL datasource:
pinGlobalTxToPhysicalConnection="true"
MariaDB's java driver also supports this workaround since v.1.1.8
spring boot問題:
當有atomikos jta的autoconfiguration時,會自動加載jtaconfiguration,必須exclude掉。
優化:https://blog.csdn.net/wllovar/article/details/87100378