0


多数据源读写分离

本文主要涉及对技术mybatis和spring基础开源框架的使用

核心逻辑:实现拦截器,利用MappedStatement去判断sql类型,通determineCurrentLookupKey判断使用的数据源id,进行数据源的选择

配置文件(application.properties)

datasource.write.driver-class-name=com.mysql.jdbc.Driver
datasource.write.url=jdbc:mysql://172.198.16.10:3306/test
datasource.write.username=root
datasource.write.password=123456

datasource.read.driver-class-name=com.mysql.jdbc.Driver
datasource.read.url=jdbc:mysql://172.198.16.11:3306/test
datasource.read.username=root
datasource.read.password=123456

threadlocal基本方法封装

package com.iqc.manydatasource;

/**
 * @author qb
 */
public class DataSourceHolder {

    public static final String WRITE_DATASOURCE = "write";
    public static final String READ_DATASOURCE = "read";

    private static final ThreadLocal<String> local = new ThreadLocal<>();

    public static void putDataSource(String dataSource) {
        local.set(dataSource);
    }

    public static String getDataSource() {
        return local.get();
    }
}

configuration配置

package com.iqc.manydatasource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
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;
/**
 * @author qb
 */
@Configuration
public class DataSourcePropertiesConfig {

    @Primary
    @Bean("writeDataSourceProperties")
    @ConfigurationProperties("datasource.write")
    public DataSourceProperties writeDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean("readDataSourceProperties")
    @ConfigurationProperties("datasource.read")
    public DataSourceProperties readDataSourceProperties() {
        return new DataSourceProperties();
    }
}

determineCurrentLookupKey配置

package com.iqc.manydatasource;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

import static com.iqc.manydatasource.DataSourceHolder.READ_DATASOURCE;
import static com.iqc.manydatasource.DataSourceHolder.WRITE_DATASOURCE;
/**
 * @author qb
 */
@Component
public class MultipleDataSource extends AbstractRoutingDataSource {

    @Resource(name = "writeDataSourceProperties")
    private DataSourceProperties writeProperties;

    @Resource(name = "readDataSourceProperties")
    private DataSourceProperties readProperties;

    /**
     * 配置dataSourceMap资源
     */
    @Override
    public void afterPropertiesSet() {
        DataSource writeDataSource =
            writeProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
        DataSource readDataSource =
            readProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build();

        setDefaultTargetDataSource(writeDataSource);

        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put(WRITE_DATASOURCE, writeDataSource);
        dataSourceMap.put(READ_DATASOURCE, readDataSource);
        setTargetDataSources(dataSourceMap);

        super.afterPropertiesSet();
    }

    /**
     * AbstractRoutingDataSource执行determineCurrentLookupKey(),
     * 以便从一组可用的DataSource中找到合适的dataSource
     * AbstractRoutingDataSource返回该数据源的JDBC连接。
     */
    @Override
    protected Object determineCurrentLookupKey() {
        String key = DataSourceHolder.getDataSource();
        //进行置空操作,避免出现select后update导致进入下方if判断
        DataSourceHolder.putDataSource(null);
        if (key == null) {
             // default datasource
            return WRITE_DATASOURCE;
        }

        return key;
    }
}

sql拦截器配置

package com.iqc.manydatasource;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Properties;
/**
 * @author qb
 */
@Component
@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 MybatisDataSourceInterceptor implements Interceptor {
    /**
     * 进行sql拦截,根据增删改查关键字去进行数据源选择
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
        if(!synchronizationActive) {
            Object[] objects = invocation.getArgs();
            MappedStatement ms = (MappedStatement) objects[0];
            //进行sql拦截判断是UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH中的哪个。
            // select进读库,其余进写库,这里可以增加判断条件去走不同库
            if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
                DataSourceHolder.putDataSource(DataSourceHolder.READ_DATASOURCE);
            }
        }

        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
    }
}

本文转载自: https://blog.csdn.net/zcxzfeqe/article/details/125378037
版权归原作者 鱼当然得是摸的才行 所有, 如有侵权,请联系我们删除。

“多数据源读写分离”的评论:

还没有评论