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

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})

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值