Springboot 使用小记-集成 MyBatis 多数据源配置实现读写分离

0.前言

Springboot 集成 MyBatis,在纯血 Springboot 项目中增加如下依赖配置

${mybatis-starter-version}  是你要用的版本,自行替换
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>${mybatis-starter-version}</version>
</dependency>

1.单数据源

此处默认采用的数据源连接池为 HikariDataSource(据说性能很好) 。它是springboot 默认的连接池之一(Hikari、tomcat、dbcp2),具体参见

org.springframework.boot.jdbc.DataSourceBuilder 
1.1 application.properties 配置参考
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://ip:port/mytest?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
spring.datasource.username=xxxx
spring.datasource.password=xxx
spring.datasource.initial-size=2
spring.datasource.min-idle=2
spring.datasource.max-active=500
spring.datasource.max-wait=6000
spring.datasource.time-between-eviction-runs-millis=60000
spring.datasource.min-evictable-idle-time-millis=300000
spring.datasource.validation-query=select 1
spring.datasource.test-while-idle=true
spring.datasource.test-on-borrow=true
spring.datasource.test-on-return=true
spring.datasource.pool-prepared-statements=true
spring.datasource.max-open-prepared-statements=20
# mybatis 配置:实体及 mapper.xml 这里的配置类似 spring 与 mybatis 的集成,只是放到这里配置了
mybatis.type-aliases-package=com.test.app.dao.*.entity
mybatis.config-location=classpath:mybatis/mapper.xml
1.2应用类参考配置
  • 关键配置 @MapperScan(“com.test.app.dao.**.mapper”)
@Configuration
@SpringBootApplication
@MapperScan("com.test.app.dao.**.mapper")
public class DAOTestApplication extends SpringBootServletInitializer {
 
    public static void main(String[] args) {
        SpringApplication.run(DAOTestApplication.class, args);
    }
 
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        logger.info("DAO test Application is starting");
        return builder.sources(DAOTestApplication.class);
    }
}

2.多数据源(主从)

2.1 application.properteis 如下配置
# master
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.jdbcUrl=jdbc:mysql://192.168.10.1:3306/mytest?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
spring.datasource.master.username=mysql
spring.datasource.master.password=******
spring.datasource.master.minimum-idle=5
spring.datasource.master.maximum-pool-size=200
spring.datasource.master.idle-timeout=30000
spring.datasource.master.max-lifetime=1800000
spring.datasource.master.connection-timeout=30000
spring.datasource.master.connection-test-query=SELECT 1

# slave 
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave.jdbcUrl=jdbc:mysql://192.168.10.2:3306/mytest?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
spring.datasource.slave.username=mysql
spring.datasource.slave.password=******
spring.datasource.slave.minimum-idle=5
spring.datasource.slave.maximum-pool-size=200
spring.datasource.slave.idle-timeout=30000
spring.datasource.slave.max-lifetime=1800000
spring.datasource.slave.connection-timeout=30000
spring.datasource.slave.connection-test-query=SELECT 1
spring.datasource.slave.read-only=true
2.2 主从数据源的配置
@Configuration
@EnableTransactionManagement
public class MultiDataSourceConfiguration {
 
    @Primary
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
 
    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
}
2.3 Mybatis 配置动态数据源
@Configuration
@AutoConfigureAfter({MultiDataSourceConfiguration.class})
public class MyBatisConfiguration extends MybatisAutoConfiguration {
 
    public MyBatisConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
                                ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                                ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        super(properties, interceptorsProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider);
    }
 
    @Resource(name = "masterDataSource")
    private DataSource masterDataSource;
 
    @Resource(name = "slaveDataSource")
    private DataSource slaveDataSource;
 
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        return super.sqlSessionFactory(routingDataSource());
    }
 
    public AbstractRoutingDataSource routingDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new ClassLoaderRepository.SoftHashMap();
        targetDataSources.put(DynamicDataSourceHolder.MASTER, masterDataSource);
        targetDataSources.put(DynamicDataSourceHolder.SLAVE, slaveDataSource);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
}
2.4 动态数据源线程安全处理
public class DynamicDataSourceHolder {
 
    public static final String MASTER = "master";
    public static final String SLAVE = "slave";
 
    public static final ThreadLocal<String> holder = new ThreadLocal<String>();
 
    public static void setDataSource(String name) {
        holder.set(name);
    }
 
    public static String getDataSource() {
        return holder.get();
    }
}
2.5 获取动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDataSource();
    }
}
2.6 AOP 配置,在 DAO 层切入,根据方法名进行主从切换
@Component
@Aspect
public class DataSourceMethodInterceptor {
 
    private static final Logger logger = LoggerFactory.getLogger(DataSourceMethodInterceptor.class);
 
    @Before("execution(* com.test.app.dao.*.mapper.*.*(..))")
    public void dynamicSetDataSoruce(JoinPoint joinPoint) throws Exception {
        String methodName = joinPoint.getSignature().getName();
        // 查询使用从库
        if (methodName.startsWith("select") || methodName.startsWith("load") || methodName.startsWith("get")
                || methodName.startsWith("count") || methodName.startsWith("is") || methodName.startsWith("query")
                || methodName.startsWith("search")) {
            logger.debug("methodName:{}, execute slave", methodName);
            DynamicDataSourceHolder.setDataSource("slave");
        } else { // 其它使用主库
            logger.debug("methodName:{}, execute master", methodName);
            DynamicDataSourceHolder.setDataSource("master");
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值