您的位置:首页 > 编程语言 > Java开发

Spring4+Atomikos3.9+mysql6.06使用注解实现跨库事务

2017-12-06 14:40 471 查看
maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>

<groupId>com.mutil.transaction</groupId>
<artifactId>transaction</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>transaction</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>


atomikos.properties:多个数据源配置

#####sakilaDataSource
spring.datasource.sakila.xaProperties.url=jdbc:mysql://localhost:3306/sakila?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=true
spring.datasource.sakila.xaProperties.user=root
spring.datasource.sakila.xaProperties.password=root
spring.datasource.sakila.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.sakila.minPoolSize=1
spring.datasource.sakila.maxPoolSize=10
spring.datasource.sakila.maxIdleTime=60
spring.datasource.sakila.uniqueResourceName=sakilaDataSource
spring.datasource.sakila.xaDataSourceClassName=com.mysql.cj.jdbc.MysqlXADataSource

#####libraryDataSource
spring.datasource.library.xaProperties.url=jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=true
spring.datasource.library.xaProperties.user=root
spring.datasource.library.xaProperties.password=root
spring.datasource.library.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.library.minPoolSize=1
spring.datasource.library.maxPoolSize=10
spring.datasource.library.maxIdleTime=60
spring.datasource.library.uniqueResourceName=libraryDataSource
spring.datasource.library.xaDataSourceClassName=com.mysql.cj.jdbc.MysqlXADataSource


使用注解配置DataSource、JdbcTemplate、UserTransactionManager、UserTransactionImp、JtaTransactionManager

package com.mutil.transaction.config;

import javax.sql.DataSource;
import javax.transaction.SystemException;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.jta.JtaTransactionManager;

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.jdbc.AtomikosDataSourceBean;

/**
* Created by WuTing on 2017/12/5.
*/
@Configuration
@PropertySource("classpath:atomikos.properties")
public class DBConfig {

@Primary
@Bean("libraryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.library")
public DataSource sakilaDataSource() {
return DataSourceBuilder.create().type(AtomikosDataSourceBean.class).build();
}

@Bean("sakilaDataSource")
@ConfigurationProperties(prefix = "spring.datasource.sakila")
public DataSource libraryDataSource() {
return DataSourceBuilder.create().type(AtomikosDataSourceBean.class).build();
}

@Bean("dataSource")
public DynamicDataSource dataSource() {

return new DynamicDataSource("libraryDataSource");
}

@Bean("jdbcTemplate")
public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DynamicDataSource dataSource) {

return new JdbcTemplate(dataSource);
}

@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public UserTransactionManager atomikosTransactionManager() {
UserTransactionManager atomikosTransactionManager = new UserTransactionManager();
atomikosTransactionManager.setForceShutdown(true);
return atomikosTransactionManager;
}

@Bean(name = "atomikosUserTransaction")
public UserTransactionImp atomikosUserTransaction() {
UserTransactionImp atomikosUserTransaction = new UserTransactionImp();
try {
atomikosUserTransaction.setTransactionTimeout(300);
} catch (SystemException e) {
e.printStackTrace();
}
return atomikosUserTransaction;
}

@Bean(name = "transactionManager")
public JtaTransactionManager transactionManager(UserTransactionManager atomikosTransactionManager,
UserTransactionImp atomikosUserTransaction) {
JtaTransactionManager transactionManager = new JtaTransactionManager();
transactionManager.setTransactionManager(atomikosTransactionManager);
transactionManager.setUserTransaction(atomikosUserTransaction);
transactionManager.setAllowCustomIsolationLevels(true);
return transactionManager;
}
}


DynamicDataSource:继承了AbstractRoutingDataSource,设置多个数据源,将数据源交给Spring控制

package com.mutil.transaction.config;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import com.atomikos.jdbc.AtomikosDataSourceBean;

/**
* Created by WuTing on 2017/12/5.
*/
public class DynamicDataSource extends AbstractRoutingDataSource {

public DynamicDataSource(String defaultDataSourceName) {
Map<Object, Object> dataSourcs = SpringContextUtil.getBeans(DataSource.class, DynamicDataSource.class);
List dataSourceIds = dataSourcs.keySet().stream().collect(Collectors.toList());
DataSourceContextHolder.dataSourceIds.addAll(dataSourceIds);
this.setTargetDataSources(dataSourcs);
this.setDefaultTargetDataSource(dataSourcs.get(defaultDataSourceName));
}

@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}

}


为了使用注解方式进行动态切换数据源,需要定义一个DataSource注解,并且使用AOP方式进行数据源动态切换。DataSourceAspect 切面便是用于数据源切换。

DataSource注解:可作用于方法、class上, 用于class表明当前class的所有方法使用相同的数据源,用于方法上申明当前方法使用的数据源,当同时用于方法和class上时,用于方法上优先级较高。

package com.mutil.transaction.config;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by WuTing on 2017/12/5.
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DataSource {
String value();
}


DataSourceAspect:用于动态切换数据源.

package com.mutil.transaction.config;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
* Created by WuTing on 2017/12/5.
*/
@Component
@Aspect
@Order(1)
public class DataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

@Before(value = "execution(* com.mutil.transaction.service.*.*(..))")
public void before(JoinPoint point) {
DataSource annotation = getDataSourceAnnotation(point);
if (annotation != null) {
DataSourceContextHolder.setDataSourceType(annotation.value());
logger.debug("Set DataSource : {} > {}", annotation.value(), point.getSignature());
}
}

@After(value = "execution(* com.mutil.transaction.service.*.*(..))")
public void restoreDataSource(JoinPoint point) {

DataSource annotation = getDataSourceAnnotation(point);
if (annotation != null) {
logger.debug("Revert DataSource : {} > {}", annotation.value(), point.getSignature());
}
DataSourceContextHolder.clearDataSourceType();
}

private DataSource getDataSourceAnnotation(JoinPoint point) {
DataSource annotation = null;
Class[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes();
String methodName = point.getSignature().getName();
try {
Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
if (method.isAnnotationPresent(DataSource.class)) {
annotation = method.getAnnotation(DataSource.class);
} else {
annotation = point.getTarget().getClass().getAnnotation(DataSource.class);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}

return annotation;
}
}


DataSourceContextHolder:数据源切换实现类

package com.mutil.transaction.config;

import java.util.ArrayList;
import java.util.List;

/**
* Created by WuTing on 2017/12/5.
*/
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static List<String> dataSourceIds = new ArrayList<>();

public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}

public static String getDataSourceType() {
return contextHolder.get();
}

public static void clearDataSourceType() {
contextHolder.remove();
}

public static boolean containsDataSource(String dataSourceId) {
return dataSourceIds.contains(dataSourceId);
}
}


MultiTransactional注解:用于跨库事务,仅仅可用于方法上,用于方法上表明当前方法所有操作属于同一个事务。

package com.mutil.transaction.config;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;

/**
* Created by WuTing on 2017/12/6.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MultiTransactional {

@AliasFor("transactionManager")
String value() default "";

@AliasFor("value")
String transactionManager() default "";

Class<? extends Throwable>[] rollbackFor() default {};

Class<? extends Throwable>[] noRollbackFor() default {};
}


MultiTransactionalAspect:事务处理,减少重复代码编写。仅当请求方法有MultiTransactional注解时才会进入。

package com.mutil.transaction.config;

import java.lang.reflect.Method;
import java.util.Arrays;

import javax.transaction.UserTransaction;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.jta.JtaTransactionManager;

/**
* Created by WuTing on 2017/12/6.
*/
@Aspect
@Component
public class MultiTransactionalAspect {

private static final Logger logger = LoggerFactory.getLogger(MultiTransactionalAspect.class);

@Around(value = "@annotation(com.mutil.transaction.config.MultiTransactional)")
public Object transactional(ProceedingJoinPoint point) throws Exception {
String methodName = point.getSignature().getName();
Class[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes();
UserTransaction tran = null;
Object result = null;
MultiTransactional multiTransactional = null;
try {
Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);

if (method.isAnnotationPresent(MultiTransactional.class)) {
multiTransactional = method.getAnnotation(MultiTransactional.class);
JtaTransactionManager transactionManager = SpringContextUtil.getBean(JtaTransactionManager.class);
tran = transactionManager.getUserTransaction();
tran.begin();
logger.warn(methodName + ", transaction begin");
result = point.proceed();
tran.commit();
logger.warn(methodName + ", transaction commit");
}

} catch (Throwable e) {
logger.error(e.getMessage(), e);

if (tran != null) {
Class<? extends Throwable>[] rollbackExcptions = multiTransactional.rollbackFor();
Class<? extends Throwable>[] noRollbackExcptions = multiTransactional.noRollbackFor();
boolean rollback = isPresent(e, rollbackExcptions);
boolean noRollback = isPresent(e, noRollbackExcptions);

if (rollback || !noRollback) {
tran.rollback();
logger.warn(methodName + ", transaction rollback");
} else {
tran.commit();
logger.warn(methodName + ", transaction commit");
}
}
}

return result;
}

private boolean isPresent(Throwable e, Class<? extends Throwable>[] excptions) {
return Arrays.stream(excptions)
.filter(exception -> e.getClass().isAssignableFrom(exception) || e.getClass().equals(exception))
.findAny()
.isPresent();
}
}


TestServiceImpl:测试将数据保存到不同的库(sakila,library)。

package com.mutil.transaction.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.jta.JtaTransactionManager;

import com.mutil.transaction.config.MultiTransactional;
import com.mutil.transaction.model.Actor;
import com.mutil.transaction.model.Author;

/**
* Created by WuTing on 2017/12/5.
*/
@Service
public class TestServiceImpl implements TestService {

@Autowired
private ActorService actorService;
@Autowired
private AuthorService authorService;

@Autowired
@Qualifier("transactionManager")
private JtaTransactionManager transactionManager;

@MultiTransactional(rollbackFor = RuntimeException.class)
@Override
public Boolean saveDatas() {
authorService.saveAuthor(); //library库
actorService.saveActor();  //sakila库
//      重构到MultiTransactionalAspect中
//      UserTransaction tran = transactionManager.getUserTransaction();
//      try {
//          tran.begin();
//          authorService.saveAuthor();
//          actorService.saveActor();
//          tran.commit();
//      } catch (Exception e) {
//          e.printStackTrace();
//          try {
//              tran.rollback();
//          } catch (SystemException e1) {
//              e1.printStackTrace();
//          }
//      }
return true;
}
}


ActorServiceImpl

package com.mutil.transaction.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import com.mutil.transaction.config.DataSource;

/**
* Created by WuTing on 2017/12/5.
*/
@Service
@DataSource("sakilaDataSource") //切库
public class ActorServiceImpl implements ActorService {
@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public boolean saveActor() {
jdbcTemplate.execute(
"insert into actor(actor_id, first_name, last_name, last_update) values(999, 'wwww', 'baidu', '2017-12-05  00:00:00')");
//      throw new RuntimeException();
return true;
}
}


package com.mutil.transaction.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import com.mutil.transaction.config.DataSource;

/**
* Created by WuTing on 2017/12/5.
*/
@Service
@DataSource("libraryDataSource") //切库
public class AuthorServiceImpl implements AuthorService {
@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public boolean saveAuthor() {
jdbcTemplate.execute("insert into author(id, first_name, last_name) values(999, 'wwww', 'baidu')");
return true;
}
}


事务成功日志:

2017-12-06 14:12:18.132  INFO 6748 --- [           main] c.a.icatch.imp.thread.TaskManager        : THREADS: using JDK thread pooling...
2017-12-06 14:12:18.140  INFO 6748 --- [           main] c.a.icatch.imp.BaseTransactionManager    : createCompositeTransaction ( 300000 ): created new ROOT transaction with id 10.34.135.125.tm0000100069
2017-12-06 14:12:18.141  WARN 6748 --- [           main] c.m.t.config.MultiTransactionalAspect    : saveDatas, transaction begin
2017-12-06 14:12:18.158  INFO 6748 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': getConnection ( null )...
2017-12-06 14:12:18.158  INFO 6748 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': init...
2017-12-06 14:12:18.158  WARN 6748 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': poolSize equals default - this may cause performance problems!
2017-12-06 14:12:18.172  INFO 6748 --- [           main] c.atomikos.jdbc.AtomikosDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': initializing with [ xaDataSourceClassName=com.mysql.cj.jdbc.MysqlXADataSource, uniqueResourceName=libraryDataSource, maxPoolSize=10, minPoolSize=1, borrowConnectionTimeout=30, maxIdleTime=60, reapTimeout=0, maintenanceInterval=60, testQuery=null, xaProperties=[user=root,password=root,url=jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=true], loginTimeout=0, maxLifetime=0]
2017-12-06 14:12:18.349  INFO 6748 --- [           main] c.a.d.xa.XATransactionalResource         : libraryDataSource: refreshed XAResource
2017-12-06 14:12:18.384  INFO 6748 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D31 ) for transaction 10.34.135.125.tm0000100069
2017-12-06 14:12:18.384  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D31 , XAResource.TMNOFLAGS ) on resource libraryDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@4d8539de
2017-12-06 14:12:18.386  INFO 6748 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@da8cecee ) for transaction 10.34.135.125.tm0000100069
2017-12-06 14:12:18.386  INFO 6748 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@a619c2: calling createStatement...
2017-12-06 14:12:18.392  INFO 6748 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@a619c2: close()...
2017-12-06 14:12:18.392  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D31 , XAResource.TMSUCCESS ) on resource libraryDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@4d8539de
2017-12-06 14:12:18.397  INFO 6748 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'sakilaDataSource': getConnection ( null )...
2017-12-06 14:12:18.397  INFO 6748 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'sakilaDataSource': init...
2017-12-06 14:12:18.397  WARN 6748 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'sakilaDataSource': poolSize equals default - this may cause performance problems!
2017-12-06 14:12:18.398  INFO 6748 --- [           main] c.atomikos.jdbc.AtomikosDataSourceBean   : AtomikosDataSoureBean 'sakilaDataSource': initializing with [ xaDataSourceClassName=com.mysql.cj.jdbc.MysqlXADataSource, uniqueResourceName=sakilaDataSource, maxPoolSize=10, minPoolSize=1, borrowConnectionTimeout=30, maxIdleTime=60, reapTimeout=0, maintenanceInterval=60, testQuery=null, xaProperties=[user=root,password=root,url=jdbc:mysql://localhost:3306/sakila?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=true], loginTimeout=0, maxLifetime=0]
2017-12-06 14:12:18.405  INFO 6748 --- [           main] c.a.d.xa.XATransactionalResource         : sakilaDataSource: refreshed XAResource
2017-12-06 14:12:18.411  INFO 6748 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D32 ) for transaction 10.34.135.125.tm0000100069
2017-12-06 14:12:18.411  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D32 , XAResource.TMNOFLAGS ) on resource sakilaDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@18fdb6cf
2017-12-06 14:12:18.412  INFO 6748 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@da8cecee ) for transaction 10.34.135.125.tm0000100069
2017-12-06 14:12:18.412  INFO 6748 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@61533ae: calling createStatement...
2017-12-06 14:12:18.413  INFO 6748 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@61533ae: close()...
2017-12-06 14:12:18.413  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D32 , XAResource.TMSUCCESS ) on resource sakilaDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@18fdb6cf
2017-12-06 14:12:18.415  INFO 6748 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : commit() done (by application) of transaction 10.34.135.125.tm0000100069
2017-12-06 14:12:18.418  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.prepare ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D31 ) returning OK on resource libraryDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@4d8539de
2017-12-06 14:12:18.421  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.prepare ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D32 ) returning OK on resource sakilaDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@18fdb6cf
2017-12-06 14:12:18.458  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.commit ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D31 , false ) on resource libraryDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@4d8539de
2017-12-06 14:12:18.461  INFO 6748 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.commit ( 31302E33342E3133352E3132352E746D30303030313030303639:31302E33342E3133352E3132352E746D32 , false ) on resource sakilaDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@18fdb6cf
2017-12-06 14:12:18.463  WARN 6748 --- [           main] c.m.t.config.MultiTransactionalAspect    : saveDatas, transaction commit
2017-12-06 14:12:18.467  INFO 6748 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cd76f31: startup date [Wed Dec 06 14:12:16 CST 2017]; root of context hierarchy
2017-12-06 14:12:18.469  INFO 6748 --- [       Thread-2] c.a.persistence.imp.AbstractLogStream    : Logfile closed: F:\code\transaction\.\tmlog68.log
2017-12-06 14:12:18.475  INFO 6748 --- [       Thread-2] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'sakilaDataSource': close...
2017-12-06 14:12:18.476  INFO 6748 --- [       Thread-2] c.a.datasource.pool.ConnectionPool       : atomikos connection pool 'sakilaDataSource': destroying pool...
2017-12-06 14:12:18.477  INFO 6748 --- [       Thread-2] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': close...
2017-12-06 14:12:18.477  INFO 6748 --- [       Thread-2] c.a.datasource.pool.ConnectionPool       : atomikos connection pool 'libraryDataSource': destroying pool...


事务失败日志:

2017-12-06 14:13:20.888  INFO 27904 --- [           main] c.a.icatch.imp.thread.TaskManager        : THREADS: using JDK thread pooling...
2017-12-06 14:13:20.897  INFO 27904 --- [           main] c.a.icatch.imp.BaseTransactionManager    : createCompositeTransaction ( 300000 ): created new ROOT transaction with id 10.34.135.125.tm0000100070
2017-12-06 14:13:20.898  WARN 27904 --- [           main] c.m.t.config.MultiTransactionalAspect    : saveDatas, transaction begin
2017-12-06 14:13:20.914  INFO 27904 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': getConnection ( null )...
2017-12-06 14:13:20.914  INFO 27904 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': init...
2017-12-06 14:13:20.915  WARN 27904 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': poolSize equals default - this may cause performance problems!
2017-12-06 14:13:20.927  INFO 27904 --- [           main] c.atomikos.jdbc.AtomikosDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': initializing with [ xaDataSourceClassName=com.mysql.cj.jdbc.MysqlXADataSource, uniqueResourceName=libraryDataSource, maxPoolSize=10, minPoolSize=1, borrowConnectionTimeout=30, maxIdleTime=60, reapTimeout=0, maintenanceInterval=60, testQuery=null, xaProperties=[user=root,password=root,url=jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=true], loginTimeout=0, maxLifetime=0]
2017-12-06 14:13:21.083  INFO 27904 --- [           main] c.a.d.xa.XATransactionalResource         : libraryDataSource: refreshed XAResource
2017-12-06 14:13:21.116  INFO 27904 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 31302E33342E3133352E3132352E746D30303030313030303730:31302E33342E3133352E3132352E746D31 ) for transaction 10.34.135.125.tm0000100070
2017-12-06 14:13:21.116  INFO 27904 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 31302E33342E3133352E3132352E746D30303030313030303730:31302E33342E3133352E3132352E746D31 , XAResource.TMNOFLAGS ) on resource libraryDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@1a411233
2017-12-06 14:13:21.118  INFO 27904 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@da8ced04 ) for transaction 10.34.135.125.tm0000100070
2017-12-06 14:13:21.118  INFO 27904 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@3605c4d3: calling createStatement...
2017-12-06 14:13:21.127  INFO 27904 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@3605c4d3: close()...
2017-12-06 14:13:21.127  INFO 27904 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 31302E33342E3133352E3132352E746D30303030313030303730:31302E33342E3133352E3132352E746D31 , XAResource.TMSUCCESS ) on resource libraryDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@1a411233
2017-12-06 14:13:21.132  INFO 27904 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
2017-12-06 14:13:21.190  INFO 27904 --- [           main] o.s.jdbc.support.SQLErrorCodesFactory    : SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
2017-12-06 14:13:21.190  INFO 27904 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': getConnection ( null )...
2017-12-06 14:13:21.191  INFO 27904 --- [           main] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': init...
2017-12-06 14:13:21.191  INFO 27904 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@3605c4d3: calling getMetaData...
2017-12-06 14:13:21.191  INFO 27904 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.cj.jdbc.ConnectionWrapper@3605c4d3: close()...
2017-12-06 14:13:21.196 ERROR 27904 --- [           main] c.m.t.config.MultiTransactionalAspect    : StatementCallback; SQL [insert into author(id, first_name, last_name) values(999, 'wwww', 'baidu')]; Duplicate entry '999' for key 'PRIMARY'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '999' for key 'PRIMARY'

org.springframework.dao.DuplicateKeyException: StatementCallback; SQL [insert into author(id, first_name, last_name) values(999, 'wwww', 'baidu')]; Duplicate entry '999' for key 'PRIMARY'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '999' for key 'PRIMARY'
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:239) ~[spring-jdbc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73) ~[spring-jdbc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:419) ~[spring-jdbc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:445) ~[spring-jdbc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at com.mutil.transaction.service.AuthorServiceImpl.saveAuthor(AuthorServiceImpl.java:20) ~[classes/:na]
at com.mutil.transaction.service.AuthorServiceImpl$$FastClassBySpringCGLIB$$156f1815.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at com.mutil.transaction.service.AuthorServiceImpl$$EnhancerBySpringCGLIB$$a3ec647.saveAuthor(<generated>) ~[classes/:na]
at com.mutil.transaction.service.TestServiceImpl.saveDatas(TestServiceImpl.java:70) ~[classes/:na]
at com.mutil.transaction.service.TestServiceImpl$$FastClassBySpringCGLIB$$f5a0a96e.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at com.mutil.transaction.config.MultiTransactionalAspect.transactional(MultiTransactionalAspect.java:42) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) [spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at com.mutil.transaction.service.TestServiceImpl$$EnhancerBySpringCGLIB$$e95da300.saveDatas(<generated>) [classes/:na]
at com.mutil.transaction.TransactionApplicationTests.saveDatas(TransactionApplicationTests.java:55) [test-classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) [spring-test-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12]
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) [junit-rt.jar:na]
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) [junit-rt.jar:na]
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) [junit-rt.jar:na]
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) [junit-rt.jar:na]
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '999' for key 'PRIMARY'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:533) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:513) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:115) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1983) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1936) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:891) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:795) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at com.mysql.cj.jdbc.StatementWrapper.execute(StatementWrapper.java:461) ~[mysql-connector-java-6.0.6.jar:6.0.6]
at org.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:436) ~[spring-jdbc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:408) ~[spring-jdbc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
... 66 common frames omitted

2017-12-06 14:13:21.201  INFO 27904 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.rollback ( 31302E33342E3133352E3132352E746D30303030313030303730:31302E33342E3133352E3132352E746D31 ) on resource libraryDataSource represented by XAResource instance com.mysql.cj.jdbc.MysqlXAConnection@1a411233
2017-12-06 14:13:21.203  INFO 27904 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : rollback() done of transaction 10.34.135.125.tm0000100070
2017-12-06 14:13:21.203  WARN 27904 --- [           main] c.m.t.config.MultiTransactionalAspect    : saveDatas, transaction rollback
2017-12-06 14:13:21.206  INFO 27904 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cd76f31: startup date [Wed Dec 06 14:13:19 CST 2017]; root of context hierarchy
2017-12-06 14:13:21.208  INFO 27904 --- [       Thread-2] c.a.persistence.imp.AbstractLogStream    : Logfile closed: F:\code\transaction\.\tmlog69.log
2017-12-06 14:13:21.213  INFO 27904 --- [       Thread-2] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'sakilaDataSource': close...
2017-12-06 14:13:21.213  INFO 27904 --- [       Thread-2] c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'libraryDataSource': close...
2017-12-06 14:13:21.213  INFO 27904 --- [       Thread-2] c.a.datasource.pool.ConnectionPool       : atomikos connection pool 'libraryDataSource': destroying pool...


SpringContextUtil, SpringContext工具类,用于获取Bean

package com.mutil.transaction.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.atomikos.jdbc.AtomikosDataSourceBean;

/**
* Created by WuTing on 2017/12/5.
*/
@Component
public class SpringContextUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}

public static ApplicationContext getApplicationContext() {
return applicationContext;
}

public static <T> T getBean(String name) throws BeansException {
return (T)applicationContext.getBean(name);
}

public static <T> T getBean(String name, Class<?> requiredType) throws BeansException {
return applicationContext.getBean(name, (Class<T>)requiredType);
}

public static <T> T getBean(Class<?> requiredType) throws BeansException {
String[] names = applicationContext.getBeanNamesForType(requiredType);
if (names == null || names.length == 0) {
throw new IllegalArgumentException("没有找到Bean,类型:" + requiredType.getName());
}
return (T)applicationContext.getBean(names[0]);
}

public static <T> Collection<T> getBeans(String... names) throws BeansException {
Collection<T> beans = new ArrayList<>();
for (String name : names) {
beans.add((T)applicationContext.getBean(name));
}
return beans;
}

public static <T> Map<Object, T> getBeans(Class<?> requiredType) throws BeansException {
Map<Object, T> beans = new HashMap<>();
String[] names = applicationContext.getBeanNamesForType(requiredType);
for (String name : names) {
beans.put(name, (T)applicationContext.getBean(name));
}
return beans;
}

public static <T> Map<Object, T> getBeans(Class<?> requiredType, Class<?>... excludeTypes) throws BeansException {
Map<Object, T> beans = new HashMap<>();
String[] names = applicationContext.getBeanNamesForType(requiredType);
for (String name : names) {
Class type = applicationContext.getType(name);
if (excludeTypes != null) {
boolean flag = Arrays.stream(excludeTypes)
.filter(excludeType -> excludeType.equals(type))
.findAny()
.isPresent();

if (flag) {
continue;
}
}
beans.put(name, (T)applicationContext.getBean(name));
}
return beans;
}

public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}

public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return applicationContext.isSingleton(name);
}

public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return applicationContext.getType(name);
}

public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return applicationContext.getAliases(name);
}
}


多数据源报错 :参考http://blog.csdn.net/gudejundd/article/details/52871432

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 3: libraryDataSource,sakilaDataSource,dataSource
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1092)
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init(DataSourceInitializer.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:134)
... 104 more


错误原因:

有个DataSourceAutoConfiguration 会初始化DataSourceInitializer 这个类 ,这个类有一个init方法 会去获取DataSource(数据源)





初始化方法中 会获取数据源 需要初始化一些ddl操作 也是就runSchemaScripts()方法 检查初始化时是否需要执行sql script ,当你有多个数据源的时候,程序不知道取哪一个 ,所以报错

解决办法:

1.定义数据源的地方 加个@primary注解, 记得只给其中的一个加, 当多数据源时 标示这个数据源是主要的

2. spring boot 启动类加上 exclude = DataSourceAutoConfiguration.class 代表启动项目的时候 不加载这个类

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: