Skip to content

Dynamic_DataSource

睿驰 edited this page Mar 26, 2021 · 6 revisions

动态数据源使用示例。

完整示例代码:juice-samples

1.添加依赖

首先,添加juice-dynamic-datasource依赖:

<juice.version>1.0.0</juice.version>

<dependency>
    <groupId>io.infinityclub</groupId>
    <artifactId>juice-dynamic-datasource</artifactId>
    <version>${juice.version}</version>
</dependency>

<dependency>
    <groupId>io.infinityclub</groupId>
    <artifactId>juice-spring-boot-starter</artifactId>
    <version>${juice.version}</version>
</dependency>

2.application.yml配置

application.yml增加如下配置:

spring:
  datasource:
    member:
      master:
        driver-class-name: com.mysql.cj.jdbc.Driver
        filters: mergeStat
        initial-size: 2
        max-active: 100
        max-wait: 20000
        min-evictable-idle-time-millis: 300000
        min-idle: 1
        name: member_master
        username: root2021
        password: root2021
        test-on-borrow: false
        test-on-return: false
        test-while-idle: true
        time-between-eviction-runs-millis: 60000
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://10.10.175.136:3306/member_master?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
        validation-query: SELECT 'x'
      slave:
        driver-class-name: com.mysql.cj.jdbc.Driver
        filters: mergeStat
        initial-size: 2
        max-active: 100
        max-wait: 20000
        min-evictable-idle-time-millis: 300000
        min-idle: 1
        name: member_slave
        username: root2021
        password: root2021
        test-on-borrow: false
        test-on-return: false
        test-while-idle: true
        time-between-eviction-runs-millis: 60000
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://10.10.175.136:3306/member_slave?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
        validation-query: SELECT 'x'
    product:
      master:
        driver-class-name: com.mysql.cj.jdbc.Driver
        filters: mergeStat
        initial-size: 2
        max-active: 100
        max-wait: 20000
        min-evictable-idle-time-millis: 300000
        min-idle: 1
        name: product_master
        username: root2021
        password: root2021
        test-on-borrow: false
        test-on-return: false
        test-while-idle: true
        time-between-eviction-runs-millis: 60000
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://10.10.175.136:3306/product_master?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
        validation-query: SELECT 'x'
      slave:
        driver-class-name: com.mysql.cj.jdbc.Driver
        filters: mergeStat
        initial-size: 2
        max-active: 100
        max-wait: 20000
        min-evictable-idle-time-millis: 300000
        min-idle: 1
        name: product_slave
        username: root2021
        password: root2021
        test-on-borrow: false
        test-on-return: false
        test-while-idle: true
        time-between-eviction-runs-millis: 60000
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://10.10.175.136:3306/product_slave?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
        validation-query: SELECT 'x'

# mybatis的相关配置
mybatis:
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  type-aliases-package: juice.samples.storage.entity
  configLocation: classpath:mybatis/mybatis-config.xml

3.数据源配置

package juice.samples.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import juice.datasource.DynamicDataSource;
import juice.samples.constants.DataSourceKey;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

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

/**
 * @author Ricky Fung
 */
@Configuration
@MapperScan(value = "juice.samples.storage.**.mapper", sqlSessionFactoryRef = "sqlSessionFactory")
public class DruidDataSourceConfig {

    @Value("${mybatis.mapper-locations:}")
    private String mapperLocations;

    @Value("${mybatis.type-aliases-package:}")
    private String typeAliasesPackage;

    @Value("${mybatis.configLocation:}")
    private String configLocation;

    @Bean
    public DynamicDataSource dynamicDataSource() {
        Map<String,String> pkgDefaultDsKeyMap = new HashMap<>(4);
        pkgDefaultDsKeyMap.put("juice.samples.storage.mapper.member", DataSourceKey.MASTER_MEMBER);
        pkgDefaultDsKeyMap.put("juice.samples.storage.mapper.product", DataSourceKey.MASTER_PRODUCT);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> tarDsMap = new HashMap<>(8);
        tarDsMap.put(DataSourceKey.MASTER_MEMBER, memberMasterDS());
        tarDsMap.put(DataSourceKey.SLAVE_MEMBER, memberSlaveDS());
        tarDsMap.put(DataSourceKey.MASTER_PRODUCT, productMasterDS());
        tarDsMap.put(DataSourceKey.SLAVE_PRODUCT, productSlaveDS());
        dynamicDataSource.setTargetDataSources(tarDsMap);

        dynamicDataSource.setDefaultTargetDataSource(tarDsMap.get(DataSourceKey.MASTER_MEMBER));

        dynamicDataSource.setPkgDefaultDsKeyMap(pkgDefaultDsKeyMap);

        return dynamicDataSource;
    }

    //==========
    @Bean
    @ConfigurationProperties("spring.datasource.member.master")
    public DataSource memberMasterDS(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.member.slave")
    public DataSource memberSlaveDS(){
        return DruidDataSourceBuilder.create().build();
    }

    //==========
    @Bean
    @ConfigurationProperties("spring.datasource.product.master")
    public DataSource productMasterDS(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.product.slave")
    public DataSource productSlaveDS(){
        return DruidDataSourceBuilder.create().build();
    }
    
    //===========
    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        bean.setTypeAliasesPackage(typeAliasesPackage);
        bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(configLocation));
        return bean.getObject();
    }

}

最后,在 Application类加上 @EnableDynamicDataSource 注解,如下:

package juice.samples;

import juice.datasource.annotation.EnableDynamicDataSource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author Ricky Fung
 */
//开启动态数据源
@EnableDynamicDataSource(basePackages = {"juice.samples.storage.mapper", "juice.samples.storage.manager"})
@SpringBootApplication
public class JuiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(JuiceApplication.class, args);
    }
}

4.使用@DSRouting

在需要切换数据源的类或者方法上加上 @DSRouting 注解即可,优先级:方法 > 类 > 包配置;

加在类上:

@DSRouting(DataSourceKey.MASTER_MEMBER)
public interface MemberMapper {

}

或者在方法上:

@Component
public class MemberManager {
    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Resource
    private MemberMapper memberMapper;

    @DSRouting(DataSourceKey.MASTER_MEMBER)
    public Member findById(Long id) {
        return memberMapper.selectByPrimaryKey(id);
    }

}

结合@Transactional一起使用,如下:

    @DSRouting(value = DataSourceKey.MASTER_BYPASS)
    @Transactional
    public int deleteBill(Long billId) {
        int rows = customerOrderBillMapper.updateDelete(billId);
        int detailRows = customerOrderBillDetailMapper.updateDelete(billId);
        //删除标记
        int goodsRows = customerOrderBillGoodsMapper.deleteByBillId(billId);
        LOG.info("甲方账单-删除账单, billId:{} 删除账单数量:{}, 账单详情数量:{}, 订单商品数量:{}", billId, rows, detailRows, goodsRows);
        return rows;
    }

Clone this wiki locally