spring boot 二十六 (mybatis 实现多数据源 轮询配置)

本文介绍如何在Spring Boot中实现多数据源配置及读写分离,包括去除默认MyBatis配置、使用动态数据源、配置多数据源等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码地址:https://gitee.com/leonzhang2013/mybaits__multiple_data_sources

实现原理:

关键点一 (去掉spring boot 默认的mybatis 自动配置)

spring boot 集成 mybatis 内部默配置只能使用 单实例的数据源。但是在实现多数据源时。不能使用 默认的配置,首先要去掉spring boot 默认的mybatis 自动配置:

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MybatisApplication {
	public static void main(String[] args) {
		SpringApplication.run(MybatisApplication.class, args);
	}
}

关键点二 (使用动态数据源)

如果我们要实现多数据库读写分离,就需要查询或修改的数据的时候,调用对应的数据库,spring boot jdbc 提供了一个 AbstractRoutingDataSource,通过实现,我们可以在操作数据库之前,动态的设置 数据源:

public class DynamicDataSource extends AbstractRoutingDataSource{
    @Override
    protected Object determineCurrentLookupKey() {
        String dataSourceType = DatabaseContextHolder.getDataSourceType();
        System.out.println("动态获取到的 数据源key == "+dataSourceType);
        return dataSourceType;
    }
}

关键点三 (使用 ThreadLoacl 实现信息传递)

DatabaseContextHolder 内部使用 ThreadLocal 类,通过ThreadLocal 可以给每个线程设置和获取数据,起作用是在 AOP拦截到对应的 方法时,实现读写分离。

public class DatabaseContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal();
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }
    public static String getDataSourceType() {
        return contextHolder.get();
    }
}

关键点四(配置多数据源)

多数据源 使用默认的 properties配置肯定是不行的了,这里就需要我们使用 自定义的配置。
在 resouce 目录下创建一个 multidatabase.properties 文件,内容如下。

#下面的main  和 read 数据将会以轮询的方式 被 访问。
#mian 循环main
#read 循环read  具体代码查看DataSourceAOP

#自定义多数据源配置
my.datasource.driver=com.mysql.jdbc.Driver

#  main 代表主服务器 可读写   read  = 只读
my.datasource[0].type=main
my.datasource[0].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop
my.datasource[0].username=lichuan
my.datasource[0].password=2018515

my.datasource[1].type=read
my.datasource[1].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop1
my.datasource[1].username=lichuan
my.datasource[1].password=2018515

my.datasource[2].type=read
my.datasource[2].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop
my.datasource[2].username=lichuan
my.datasource[2].password=2018515

my.datasource[3].type=main
my.datasource[3].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop1
my.datasource[3].username=lichuan
my.datasource[3].password=2018515

关键点五(读取配置文件)

@Configuration
@MapperScan(basePackages = "com.example.mybatis.mapper")
@PropertySource(value = "classpath:multidatabase.properties", encoding = "utf-8")
@ConfigurationProperties("my")
@Data
public class MultDataSource {
    public static final String MAIN = "main";
    public static final String READ = "read";

    public List<String> mainKeys = new ArrayList<>();
    public List<String> readKeys = new ArrayList<>();

    @Value("${my.datasource.driver}")
    private String driver;

    /**
     *  读取配置文件获取。
     */
    private List<MyDatabase> datasource;

    public DruidDataSource getDataSource(MyDatabase database) {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(database.getUrl());
        druidDataSource.setUsername(database.getUsername());
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setPassword(database.getPassword());
        druidDataSource.setInitialSize(1);
        druidDataSource.setMaxWait(6000);
        druidDataSource.setMinIdle(8);
        return druidDataSource;
    }

    @Bean
    @Primary
    public DynamicDataSource dataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        for (int i = 0; i < datasource.size(); i++) {
            String type = datasource.get(i).getType();
            DruidDataSource dataSource = getDataSource(datasource.get(i));
            if (MAIN.equals(type)) {
                mainKeys.add(MAIN+i);
                targetDataSources.put(MAIN+i,dataSource);
            } else {
                readKeys.add(READ+i);
                targetDataSources.put(READ+i,dataSource);
            }
        }
        DynamicDataSource dataSource = new DynamicDataSource();
        // 该方法是AbstractRoutingDataSource的方法
        dataSource.setTargetDataSources(targetDataSources);
        // 默认的datasource设置为myTestDbDataSource
        dataSource.setDefaultTargetDataSource(targetDataSources.get(mainKeys.get(0)));
        return dataSource;
    }
}

关键点六(AOP 拦截代码)

UserMapper 类 这里没有贴出 mapper.xml

package com.example.mybatis.mapper;
@Component
@Mapper
public interface UserMapper {
    int deleteByPrimaryKey(Long id);
    int insert(User record);
    User selectByPrimaryKey(Long id);
    List<User> selectAll();
    int updateByPrimaryKey(User record);
}

AOP 拦截类

@Aspect
@Component
public class DataSourceAop {

    @Autowired
    MultDataSource multDataSource;


    @Before("execution(* com.example.mybatis.mapper..*.get*(..)) || " +
            "execution(* com.example.mybatis.mapper..*.list*(..)) ||"+
            "execution(* com.example.mybatis.mapper..*.select*(..))")
    public  void setReadDataSource(){
        DatabaseContextHolder.setDataSourceType(getReadKey());
        System.out.println("我是读");
    }

    @Before("execution(* com.example.mybatis.mapper..*.add*(..)) || " +
            "execution(* com.example.mybatis.mapper..*.update*(..)) ||"+
            "execution(* com.example.mybatis.mapper..*.insert*(..)) ||"+
            "execution(* com.example.mybatis.mapper..*.delete*(..))")
    public  void setWriteDataSource(){
        DatabaseContextHolder.setDataSourceType(getMainKey());
        System.out.println("我是写");
    }

    /**
     * 轮询方式
     */
    int m = 0;
    public String getMainKey(){
        List<String> readKeys = multDataSource.getMainKeys();
        m ++;
        m = m%readKeys.size();
        return readKeys.get( m );
    }

    int i = 0;
    public String getReadKey(){
        List<String> readKeys = multDataSource.getReadKeys();
        i ++;
        i = i%readKeys.size();
       return readKeys.get( i );
    }
}

需要的依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.7</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值