springboot读写分离配置

1.设置多数据源

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    druid:
      read:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3310/jpgs?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&verifyServerCertificate=false&zeroDateTimeBehavior=CONVERT_TO_NULL&tinyInt1isBit=false&allowMultiQueries=true&useAffectedRows=true
        username: root
        password: 123456
      write:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3310/jpgs?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&verifyServerCertificate=false&zeroDateTimeBehavior=CONVERT_TO_NULL&tinyInt1isBit=false&allowMultiQueries=true&useAffectedRows=true
        username: root
        password: 123456

2.编写配置类

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

    public static void clearDataSource() {
        local.remove();
    }

}
@Configuration
public class DataSourcePropertiesConfig {

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

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

//import static com.hengtiansoft.cron.config.DataSourceHolder.READ_DATASOURCE;
//import static com.hengtiansoft.cron.config.DataSourceHolder.WRITE_DATASOURCE;
@Log4j2
@Component
public class CustomRoutingDataSource extends AbstractRoutingDataSource {

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

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


    @Resource
    private Environment environment;

    private final static String DATASOURCE = "spring.datasource.druid";



    @Override
    public void afterPropertiesSet() {

        Binder binder = Binder.get(environment);
        BindResult<DruidDataSource> bindResult = binder.bind(DATASOURCE, DruidDataSource.class);
        DruidDataSource druidDataSource = bindResult.get();
        DruidDataSource writeDataSource =
                writeProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
        DruidDataSource readDataSource =
                readProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
        DruidDataSource wDruidDataSource = copyBaseDruidDataSource(WRITE_DATASOURCE,druidDataSource, writeDataSource);
        DruidDataSource rDruidDataSource = copyBaseDruidDataSource(READ_DATASOURCE,druidDataSource, readDataSource);
        setDefaultTargetDataSource(wDruidDataSource);

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

        super.afterPropertiesSet();
    }

    private static DruidDataSource copyBaseDruidDataSource(String dataSourceName,DruidDataSource druidDataSource, DruidDataSource newDataSource) {
        String url = newDataSource.getUrl();
        String username = newDataSource.getUsername();
        String password = newDataSource.getPassword();
        String driverClassName = newDataSource.getDriverClassName();
        DruidDataSource retDruidDataSource = new DruidDataSource();
        BeanUtils.copyProperties(druidDataSource, retDruidDataSource);
        retDruidDataSource.setName(dataSourceName);
        retDruidDataSource.setUrl(url);
        retDruidDataSource.setUsername(username);
        retDruidDataSource.setPassword(password);
        retDruidDataSource.setDriverClassName(driverClassName);
        return retDruidDataSource;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        String key = DataSourceHolder.getDataSource();
        log.debug("读写分离生效了=走的={}",key);
        if (key == null) {
            // default datasource
            return WRITE_DATASOURCE;
        }
        if(key.equals(READ_DATASOURCE)){
            DataSourceHolder.clearDataSource();
        }
        return key;
    }


}

这时候,我们就可以在代码中做数据源的切换了。因为读取配置不包含其余配置,故我们这通过copyBaseDruidDataSource给其赋值(因配置文件结构不同,读者配置自己合适的读到对应数据即可)。注意:new DruidDataSource()必须在afterPropertiesSet方法中,不然无法走determineCurrentLookupKey方法

 DataSourceHolder.putDataSource(DataSourceHolder.READ_DATASOURCE);

4.我们在java使用mybatis时做一层代理,实现自动切换数据库

@Log4j2
@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}),
        @Signature(type = Executor.class, method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
                        CacheKey.class, BoundSql.class})})
public class MybatisDataSourceInterceptor implements Interceptor {

    private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
        log.debug("================="+synchronizationActive);
        if(synchronizationActive){
            log.debug("=================走了写");
            DataSourceHolder.putDataSource(DataSourceHolder.WRITE_DATASOURCE);
            return invocation.proceed();
        }
        Object[] objects = invocation.getArgs();
        MappedStatement ms = (MappedStatement) objects[0];
        if (!ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
            DataSourceHolder.putDataSource(DataSourceHolder.WRITE_DATASOURCE);
            return invocation.proceed();
        }
        if(ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)){
            DataSourceHolder.putDataSource(DataSourceHolder.WRITE_DATASOURCE);
            return invocation.proceed();
        }
        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) {
    }
}

完成,小伙伴们可以手动尝试下。

你可能感兴趣的:(spring,boot,java,log4j)