Bootstrap

跨库事务管理器

跨库事务管理器

本文介绍如何编写一个跨库的事务管理器,通过best effort 1pc模式在spring DataSourceTransactionManager的基础上改造出一个能够支持多数据源事务的事务管理器。

一、多数据源配置+事务管理器配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <context:annotation-config/>
    <context:component-scan base-package="com.handserome.test.dao"/>

    <!--直接配置数据库连接1-->
    <bean id="testDbDs1" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="5" />
        <property name="minIdle" value="10" />
        <property name="maxActive" value="20" />
    </bean>

    <!--直接配置数据库连接2-->
    <bean id="testDbDs2" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="5" />
        <property name="minIdle" value="10" />
        <property name="maxActive" value="20" />
    </bean>

    <bean id="shardRegister"
          class="com.handserome.daat.datasource.DefaultDbShardRegister">
        <property name="dbShardInfos">
            <set>
                <!-- 测试库1 -->
                <bean class="com.handserome.daat.datasource.DbShardInfo">
                    <property name="id" value="db1"/>
                    <property name="dataSource" ref="testDbDs1"/>
                    <property name="description" value="测试1库数据源"/>
                </bean>
                <!-- 测试库1 -->
                <bean class="com.handserome.daat.datasource.DbShardInfo">
                    <property name="id" value="db2"/>
                    <property name="dataSource" ref="testDbDs2"/>
                    <property name="description" value="测试2库数据源"/>
                </bean>
            </set>
        </property>
    </bean>

    <!-- DAL客户端接口实现 -->
    <bean id="sqlSession"
          class="com.handserome.daat.session.DbShardSqlSession">
        <property name="dbShardRegister" ref="shardRegister"/>
        <property name="sqlMapConfigLocation" value="classpath*:conf/sqlMap/sqlMap_*.xml"/>
    </bean>

    <!-- 事务配置 -->
    <bean id="transactionManager"
          class="com.handserome.daat.transaction.MultiDataSourceTransactionManager">
        <property name="dbShardRegister" ref="shardRegister"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="studentDao1" class="com.handserome.test.dao.StudentDaoImpl1">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>

二、多数据源事务管理器

/**
 * 功能描述:多数据源事务管理器<br>
 * 采用Best Effort 1PC Pattern的事务策略
 * 
 * @author handersome
 */
public class MultiDataSourceTransactionManager implements PlatformTransactionManager, InitializingBean {

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

    /**
     * 各个数据源对应的事务管理器列表
     */
    private List<DataSourceTransactionManager> transactionManagers;

    /**
     * 分库注册配置信息
     */
    private DbShardRegister dbShardRegister;

    public DbShardRegister getDbShardRegister() {
        return dbShardRegister;
    }

    public void setDbShardRegister(DbShardRegister dbShardRegister) {
        this.dbShardRegister = dbShardRegister;
    }

    /**
     *
     * 功能描述: 获取事务 每个数据源对应的事务管理器从数据源中拿到connection,并且关闭了connection的自动提交
     *
     * @param definition
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    @Override
    public MultiTransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {

        // 所有数据源事务管理里共用一个MultiTransactionStatus,因此取第0个即可
        MultiTransactionStatus mts = new MultiTransactionStatus(transactionManagers.get(0));

        // 判断当前线程的事务同步器是否存在
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            // 创建当前线程的事务同步器
            TransactionSynchronizationManager.initSynchronization();
            mts.setNewSynchronization();
        }

        try {
            // 遍历所有数据源的事务管理器
            for (DataSourceTransactionManager transactionManager : transactionManagers) {
                System.out.println(transactionManager.toString());
                // 将各数据源事务管理器注册到多事务状态中
                mts.registerTransactionManager(definition, transactionManager);
            }

        } catch (RuntimeException ex) {
            // key 具体数据源对应的事务管理器,value 该事务管理器的事务
            Map<DataSourceTransactionManager, TransactionStatus> transactionStatuses = mts.getTransactionStatuses();

            // 对各个具体的数据源事务管理器进行回滚
            for (DataSourceTransactionManager transactionManager : transactionManagers) {
                try {
                    if (transactionStatuses.get(transactionManager) != null) {
                        // 获取当前事务管理器对应的具体事务
                        transactionManager.rollback(transactionStatuses.get(transactionManager));
                    }
                } catch (Exception ex2) {
                    logger.warn("Rollback exception (" + transactionManager + ") " + ex2.getMessage(), ex2);
                }
            }

            // 发生异常清空当前线程的事务同步器
            if (mts.isNewSynchronization()) {
                TransactionSynchronizationManager.clear();
            }

            throw new CannotCreateTransactionException(ex.getMessage(), ex);
        }

        return mts;
    }

    /**
     * 功能描述:提交操作<br>
     */
    @Override
    public void commit(TransactionStatus status) throws TransactionException {

        MultiTransactionStatus multiTransactionStatus = (MultiTransactionStatus) status;

        boolean commit = true;
        Exception commitException = null;
        PlatformTransactionManager commitExceptionTransactionManager = null;

        // 反转事务管理器列表
        Iterable<DataSourceTransactionManager> reverseManagerList = reverse(transactionManagers);
        // 遍历所有数据源的事务管理器
        for (DataSourceTransactionManager transactionManager : reverseManagerList) {
            // 获取数据源对应的事务
            TransactionStatus transactionStatus = multiTransactionStatus.getTransactionStatus(transactionManager);
            // 如果存在某个数据源提交失败,后续不再提交
            if (commit) {
                try {
                    transactionManager.commit(transactionStatus);
                } catch (Exception ex) {
                    commit = false;
                    commitException = ex;
                    // 发生异常的事务管理器
                    commitExceptionTransactionManager = transactionManager;
                }

            } else {
                // 后面的事务回滚
                try {
                    transactionManager.rollback(transactionStatus);
                } catch (Exception ex) {
                    logger.warn("Rollback exception (after commit) (" + transactionManager + ") " + ex.getMessage(),
                            ex);
                }
            }
        }

        if (multiTransactionStatus.isNewSynchronization()) {
            TransactionSynchronizationManager.clear();
        }

        if (commitException != null) {
            // 判断是否是第一个事务管理器提交就失败
            boolean firstTransactionManagerFailed = commitExceptionTransactionManager == getLastTransactionManager();
            int transactionState = firstTransactionManagerFailed ? HeuristicCompletionException.STATE_ROLLED_BACK
                    : HeuristicCompletionException.STATE_MIXED;
            throw new HeuristicCompletionException(transactionState, commitException);
        }
    }

    /**
     * 功能描述:回滚操作<br>
     */
    @Override
    public void rollback(TransactionStatus status) throws TransactionException {

        Exception rollbackException = null;
        DataSourceTransactionManager rollbackExceptionTransactionManager = null;

        MultiTransactionStatus multiTransactionStatus = (MultiTransactionStatus) status;

        for (DataSourceTransactionManager transactionManager : reverse(transactionManagers)) {
            TransactionStatus transactionStatus = multiTransactionStatus.getTransactionStatus(transactionManager);
            try {
                transactionManager.rollback(transactionStatus);
            } catch (Exception ex) {
                if (rollbackException == null) {
                    rollbackException = ex;
                    rollbackExceptionTransactionManager = transactionManager;
                } else {
                    logger.warn("Rollback exception (" + transactionManager + ") " + ex.getMessage(), ex);
                }
            }
        }

        if (multiTransactionStatus.isNewSynchronization()) {
            TransactionSynchronizationManager.clear();
        }

        if (rollbackException != null) {
            throw new UnexpectedRollbackException("Rollback exception, originated at ("
                    + rollbackExceptionTransactionManager + ") " + rollbackException.getMessage(), rollbackException);
        }
    }

    @Override
    public void afterPropertiesSet() {
        Assert.notNull(dbShardRegister, "DbShardRegister must not be null!");
        transactionManagers = new ArrayList<DataSourceTransactionManager>();
        // 遍历所有的数据源,给每个数据源创建一个事务管理器,并加入到事务管理器集合中
        for (DbShardInfo dbShardInfo : dbShardRegister.getDbShardInfos()) {
            DataSourceTransactionManager txManager = new DataSourceTransactionManager(dbShardInfo.getDataSource());
            transactionManagers.add(txManager);
        }

    }

    private <T> Iterable<T> reverse(Collection<T> collection) {

        List<T> list = new ArrayList<T>(collection);
        Collections.reverse(list);
        return list;
    }

    private PlatformTransactionManager getLastTransactionManager() {
        return transactionManagers.get(lastTransactionManagerIndex());
    }

    private int lastTransactionManagerIndex() {
        return transactionManagers.size() - 1;
    }

}

三、多事务状态

/**
 * 功能描述:多事务状态
 * 
 * @author handersome
 */
class MultiTransactionStatus implements TransactionStatus {

    /**
     * 主事务管理器
     */
    private final DataSourceTransactionManager mainTransactionManager;

    /**
     * key 数据源对应的事务管理器, value:具体事务
     */
    private final Map<DataSourceTransactionManager, TransactionStatus> transactionStatuses = new ConcurrentHashMap<DataSourceTransactionManager, TransactionStatus>();

    /**
     * 当前线程是否存在
     */
    private boolean newSynchronization;

    public MultiTransactionStatus(DataSourceTransactionManager mainTransactionManager) {

        Assert.notNull(mainTransactionManager, "TransactionManager must not be null!");
        this.mainTransactionManager = mainTransactionManager;
    }

    public void setNewSynchronization() {
        this.newSynchronization = true;
    }

    public boolean isNewSynchronization() {
        return newSynchronization;
    }

    public void registerTransactionManager(TransactionDefinition definition,
            DataSourceTransactionManager transactionManager) {
        // 获取数据源对应的事务状态
        TransactionStatus transaction = transactionManager.getTransaction(definition);
        transactionStatuses.put(transactionManager, transaction);
    }

    /**
     *
     * 功能描述: 获取事务管理器的当前事务
     *
     * @param transactionManager
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public TransactionStatus getTransactionStatus(DataSourceTransactionManager transactionManager) {
        return this.transactionStatuses.get(transactionManager);
    }

    @Override
    public boolean isRollbackOnly() {
        return getMainTransactionStatus().isRollbackOnly();
    }

    @Override
    public boolean isCompleted() {
        return getMainTransactionStatus().isCompleted();
    }

    @Override
    public boolean isNewTransaction() {
        return getMainTransactionStatus().isNewTransaction();
    }

    @Override
    public boolean hasSavepoint() {
        return getMainTransactionStatus().hasSavepoint();
    }

    @Override
    public void setRollbackOnly() {
        for (TransactionStatus ts : transactionStatuses.values()) {
            ts.setRollbackOnly();
        }
    }

    @Override
    public Object createSavepoint() throws TransactionException {

        SavePoints savePoints = new SavePoints();

        for (TransactionStatus transactionStatus : transactionStatuses.values()) {
            savePoints.save(transactionStatus);
        }
        return savePoints;
    }

    @Override
    public void rollbackToSavepoint(Object savepoint) throws TransactionException {
        SavePoints savePoints = (SavePoints) savepoint;
        savePoints.rollback();
    }

    @Override
    public void releaseSavepoint(Object savepoint) throws TransactionException {
        ((SavePoints) savepoint).release();
    }

    @Override
    public void flush() {
        for (TransactionStatus transactionStatus : transactionStatuses.values()) {
            transactionStatus.flush();
        }
    }

    private TransactionStatus getMainTransactionStatus() {
        return transactionStatuses.get(mainTransactionManager);
    }

    public Map<DataSourceTransactionManager, TransactionStatus> getTransactionStatuses() {
        return transactionStatuses;
    }

    /**
     * 功能描述:保存点
     * 
     * @author
     */
    private static class SavePoints {

        private final Map<TransactionStatus, Object> savePoints = new HashMap<TransactionStatus, Object>();

        private void addSavePoint(TransactionStatus status, Object savepoint) {

            Assert.notNull(status, "TransactionStatus must not be null!");
            this.savePoints.put(status, savepoint);
        }

        private void save(TransactionStatus transactionStatus) {
            Object savepoint = transactionStatus.createSavepoint();
            addSavePoint(transactionStatus, savepoint);
        }

        private void rollback() {
            for (TransactionStatus transactionStatus : savePoints.keySet()) {
                transactionStatus.rollbackToSavepoint(savepointFor(transactionStatus));
            }
        }

        private Object savepointFor(TransactionStatus transactionStatus) {
            return savePoints.get(transactionStatus);
        }

        private void release() {
            for (TransactionStatus transactionStatus : savePoints.keySet()) {
                transactionStatus.releaseSavepoint(savepointFor(transactionStatus));
            }
        }
    }
}

四、dao层实例

/**
 * @author : 18073771
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class StudentDaoImpl implements StudentDao {

    private SqlSession sqlSession;

    @Transactional
    @Override
    public void insertTransactionTest() {
        insert();
        insert1();
    }

    private void insert() {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("studentId", "222");
        paramMap.put("name", "bbb");
        paramMap.put("dbKey", "db1");
        String insert = "student.saveData";
        int execute = sqlSession.execute(insert, paramMap);
        System.out.println("插入条数" + execute);
    }

    private void insert1() {
        List<Object> objectList = new ArrayList<Object>();
        Student student = new Student();
        student.setStudentId("7878");
        student.setName("7878");
        student.setCreateTime(new Timestamp(new java.util.Date().getTime()));
        student.setDbKey("db2");
        objectList.add(student);

        Student student1 = new Student();
        student1.setStudentId("78781");
        student1.setName("78781");
        student1.setDbKey("db2");
        student1.setCreateTime(new Timestamp(new java.util.Date().getTime()));

        objectList.add(student1);
        String insert = "student.saveData";
        int i = sqlSession.batchUpdate(insert, objectList);
        System.out.println("插入条数" + i);
    }

    public SqlSession getSqlSession() {
        return sqlSession;
    }

    public void setSqlSession(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
}

五、测试方法

public class JdbcTemplateTest {

    public static void main(String[] args) throws InterruptedException {
        // 默认,非分库
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spes-da-shard.xml");
        StudentDao studentDao = (StudentDao) context.getBean("studentDao1");
        studentDao.insertTransactionTest();

        multiThreadTest(context);
    }

    private static void multiThreadTest(ClassPathXmlApplicationContext context) throws InterruptedException {
        final StudentDao studentDao = (StudentDao) context.getBean("studentDao1");
        studentDao.insertTransactionTest();
        for (int i = 0; i < 10000; i++) {
            final Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    studentDao.insertTransactionTest();
                }
            });
            thread.start();
        }
    }
}
;