使用mybatis+Spring實現mysql的讀寫分離

使用spring AbstractRoutingDatasource實現多數據源


public class DynamicDataSource extends AbstractRoutingDataSource {
    //寫數據源
    private Object writeDataSource;
    //讀數據源
    private Object readDataSource;    @Override
    public void afterPropertiesSet() {
        Assert.isNull(writeDataSource, "Property 'writeDataSource' is required");
        setDefaultTargetDataSource(writeDataSource);
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        targetDataSources.put(DBTypeEnum.WRITE.name(), writeDataSource);
        if (null != readDataSource)
            targetDataSources.put(DBTypeEnum.READ.name(), readDataSource);
        setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
 
    /**
     * 獲取當前使用那個數據源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        DBTypeEnum dataSource = DbContextHolder.getDbType();
        if (null == dataSource || dataSource == DBTypeEnum.WRITE)
            return DBTypeEnum.WRITE.name();
        return DBTypeEnum.READ.name();
    }
 
    public Object getWriteDataSource() {
        return writeDataSource;
    }
 
    public void setWriteDataSource(Object writeDataSource) {
        this.writeDataSource = writeDataSource;
    }
 
    public Object getReadDataSource() {
        return readDataSource;
    }
 
    public void setReadDataSource(Object readDataSource) {
        this.readDataSource = readDataSource;
    }
}

讀寫數據庫類型

public enum DBTypeEnum {
    WRITE, READ;
}

當前數據庫配置上下文

public class DbContextHolder {
    private static final ThreadLocal<DBTypeEnum> DATASOURCES = new ThreadLocal<DBTypeEnum>();

    private DbContextHolder() {
    }

    /**
     * 設置數據源
     *
     * @param dbType
     */
    public static void setDbType(DBTypeEnum dbType) {
        DATASOURCES.set(dbType.WRITE);
    }

    /*
    * 獲取當前數據源
    * @return DBTypeEnum
    * */
    public static DBTypeEnum getDbType() {
        return DATASOURCES.get();
    }

    /**
     * 清除上下文數據
     */
    public static void clearDbType() {
        DATASOURCES.remove();
    }
}

自定義事務管理器

public class DynamicDataSourceTransactionManager extends DataSourceTransactionManager {
    /**
     * 設置數據源
     */
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        boolean readOnly = definition.isReadOnly();
        if (readOnly)
            DbContextHolder.setDbType(DBTypeEnum.READ);
        DbContextHolder.setDbType(DBTypeEnum.WRITE);
        super.doBegin(transaction, definition);
    }

    /**
     * 清理本地線程數據源
     */
    @Override
    protected void doCleanupAfterCompletion(Object transaction) {
        super.doCleanupAfterCompletion(transaction);
        DbContextHolder.clearDbType();
    }
}

mybatis插件(攔截器)

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {
                MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {
                MappedStatement.class, Object.class, RowBounds.class,
                ResultHandler.class})})
public class DynamicPlugin implements Interceptor {
    private static final Logger _log = LoggerFactory.getLogger(DynamicPlugin.class);
    private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";
    private static final Map<String, DBTypeEnum> cachaMap = new HashMap<String, DBTypeEnum>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        boolean isSynchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
        if (!isSynchronizationActive) {
            MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
            DBTypeEnum dbTypeEnum = null;
            if ((dbTypeEnum = cachaMap.get(ms.getId())) == null) {
                //讀方法
                if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
                    //!selectKeu爲自增id查詢主鍵(SELECT LAST_INSERT_ID)方法,使用主庫
                    if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX))
                        dbTypeEnum = DBTypeEnum.WRITE;
                    else {
                        BoundSql boundSql = ms.getSqlSource().getBoundSql(invocation.getArgs()[1]);
                        String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " ");
                        if (sql.matches(REGEX))
                            dbTypeEnum = DBTypeEnum.WRITE;
                        dbTypeEnum = DBTypeEnum.READ;
                    }
                } else {
                    dbTypeEnum = DBTypeEnum.WRITE;
                }
                _log.warn("設置方法[{}] use [{}] Strategy, SqlCommandType [{}]..", ms.getId(), dbTypeEnum.name(), ms.getSqlCommandType().name());
                cachaMap.put(ms.getId(), dbTypeEnum);
            }
            DbContextHolder.setDbType(dbTypeEnum);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

原文鏈接:http://www.cnblogs.com/lovellll/p/10213947.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章