package org.hsweb.web.mybatis.dynamic; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.transaction.Transaction; import org.hsweb.web.core.datasource.DataSourceHolder; import org.hsweb.web.core.utils.ThreadLocalUtils; import org.mybatis.spring.transaction.SpringManagedTransaction; import org.springframework.jdbc.datasource.ConnectionHolder; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * mybatis 同一事务,同一个mapper,动态数据源切换支持 * * @author zhouhao */ public class DynamicSpringManagedTransaction implements Transaction { private static final Log LOGGER = LogFactory.getLog(SpringManagedTransaction.class); private Map<String, TransactionProxy> connectionMap = new HashMap<>(); /** * 当前数据源对应的事务代理 * * @return {@link TransactionProxy} */ protected TransactionProxy getProxy() { return connectionMap.get(DataSourceHolder.getActiveSourceId()); } /** * 添加一个事务代理 * * @param proxy */ protected void addProxy(TransactionProxy proxy) { connectionMap.put(DataSourceHolder.getActiveSourceId(), proxy); } /** * 获取所有代理 * * @return */ protected Collection<TransactionProxy> getAllProxy() { return connectionMap.values(); } @Override public Connection getConnection() throws SQLException { TransactionProxy proxy = getProxy(); if (proxy != null) { return proxy.getConnection(); } //根据当前激活的数据源 获取jdbc链接 DataSource dataSource = DataSourceHolder.getActiveSource(); String dsId = DataSourceHolder.getActiveSourceId(); Connection connection = DataSourceUtils.getConnection(dataSource); proxy = new TransactionProxy(dsId, connection, dataSource); addProxy(proxy); if (LOGGER.isDebugEnabled()) { LOGGER.debug( "DataSource (" + DataSourceHolder.getActiveSourceId() + ") JDBC Connection [" + connection + "] will" + (proxy.isConnectionTransactional ? " " : " not ") + "be managed by Spring"); } return connection; } @Override public void commit() throws SQLException { for (TransactionProxy proxy : getAllProxy()) { proxy.commit(); } } /** * {@inheritDoc} */ @Override public void rollback() throws SQLException { for (TransactionProxy proxy : getAllProxy()) { proxy.rollback(); } } /** * {@inheritDoc} */ @Override public void close() throws SQLException { SQLException tmp = null; for (TransactionProxy proxy : getAllProxy()) { try { proxy.close(); //保证每个链接都能被释放 } catch (SQLException e) { tmp = e; } } connectionMap.clear(); if (null != tmp) throw tmp; } @Override public Integer getTimeout() throws SQLException { return getProxy().getTimeout(); } class TransactionProxy implements Transaction { Connection connection; DataSource dataSource; boolean isConnectionTransactional; boolean autoCommit; String dataSourceId; public TransactionProxy(String dataSourceId, Connection connection, DataSource dataSource) { this.connection = connection; this.dataSource = dataSource; this.dataSourceId = dataSourceId; this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(connection, dataSource); try { this.autoCommit = connection.getAutoCommit(); } catch (SQLException e) { } } @Override public Connection getConnection() throws SQLException { return connection; } /** * {@inheritDoc} */ @Override public void commit() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Committing DataSource (" + dataSourceId + ") JDBC Connection [" + this.connection + "]"); } this.connection.commit(); } } /** * {@inheritDoc} */ @Override public void rollback() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Rolling back DataSource (" + dataSourceId + ") JDBC Connection [" + this.connection + "]"); } this.connection.rollback(); } } @Override public void close() throws SQLException { DataSourceUtils.releaseConnection(connection, dataSource); } @Override public Integer getTimeout() throws SQLException { ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (holder != null && holder.hasTimeout()) { return holder.getTimeToLiveInSeconds(); } return null; } } }