0
点赞
收藏
分享

微信扫一扫

3.springboot+mybatis动态数据源切换

转角一扇门 2022-02-18 阅读 92

Spring内置了一个AbstractRoutingDataSource,它可以把多个数据源配置成一个Map,然后,根据不同的key返回不同的数据源。因为AbstractRoutingDataSource也是一个DataSource接口,因此,应用程序可以先设置好key, 访问数据库的代码就可以从AbstractRoutingDataSource拿到对应的一个真实的数据源,从而访问指定的数据库。
我们整理一下这个类切换数据源的运作方式,这个类在连接数据库之前会执行determineCurrentLookupKey()方法,这个方法返回的数据将作为key去targetDataSources中查找相应的值,如果查找到相对应的DataSource,那么就使用此DataSource获取数据库连接它是一个abstract类,所以我们使用的话,推荐的方式是创建一个类来继承它并且实现它的determineCurrentLookupKey()方法,这个方法介绍上面也进行了说明,就是通过这个方法进行数据源的切换。

实现案例:
1.准备两个数据库

在这里插入图片描述

2.pom导入依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <!--引入适配器-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>

        <!-- 配置数据库驱动和mybatis dependency -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
2.准备数据源配置信息
spring.druid.datasource.master.password=root
spring.druid.datasource.master.username=root
spring.druid.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/springboot_master?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
spring.druid.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver

spring.druid.datasource.slave.password=root
spring.druid.datasource.slave.username=root
spring.druid.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/springboot_slave?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
spring.druid.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
3.取消数据源自动配置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})//数据源自动配置类排除
public class SpringbootDataaccessApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDataaccessApplication.class, args);
    }
}
4.准备配置类

(1)RoutingDataSourceContext

public class RoutingDataSourceContext {
    static final ThreadLocal<String> threadLocalDataSourceKey = new ThreadLocal<>();

    public static String getDataSourceRoutingKey() {
        String key = threadLocalDataSourceKey.get();
        return key == null ? "masterDataSource" : key;
    }

    public RoutingDataSourceContext(String key) {
        threadLocalDataSourceKey.set(key);
    }

    public void close() {
        threadLocalDataSourceKey.remove();
    }
}

(2)AbstractRoutingDataSource

public class RoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return RoutingDataSourceContext.getDataSourceRoutingKey();
    }
}

(3)MyDataSourceConfiguratioin

@Configuration
public class MyDataSourceConfiguratioin {

    Logger logger = LoggerFactory.getLogger(MyDataSourceConfiguratioin.class);

    /**
     *     * Master data source.    
     */
    @Bean//被@Bean标注,该方法返回值存入容器中,没有@Bean指定,所用的key默认就是方法名,即@Bean("masterDataSource")
    @ConfigurationProperties(prefix = "spring.druid.datasource.master")//数据源注入
    DataSource masterDataSource() {
        logger.info("create master datasource...");
        return DataSourceBuilder.create().build();
    }

    /**
     *     * Slave data source.    
     */
    @Bean("slaveDataSource")
    @ConfigurationProperties(prefix = "spring.druid.datasource.slave")
    DataSource slaveDataSource() {
        logger.info("create slave datasource...");
        return DataSourceBuilder.create().build();
    }

    //@Qualifier类型相同时,根据名称进行注入
    @Bean
    @Primary//三个方法返回类型相同,以此方法返回为主
    public DataSource primaryDataSource(
            @Autowired @Qualifier("masterDataSource") DataSource masterDataSourve,
            @Autowired @Qualifier("slaveDataSource") DataSource slaveDataSource
    ){
        RoutingDataSource routingDataSource = new RoutingDataSource();
        HashMap<Object, Object> map = new HashMap<>();
        map.put("master",masterDataSourve);
        map.put("slave",slaveDataSource);
        routingDataSource.setTargetDataSources(map);
        return routingDataSource;
    }
}
5.准备实体类
public class Product {

    private int id;

    private String name;

    private int num;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", num=" + num +
                '}';
    }
}

6.准备Mapper
@Mapper
public interface ProductDao {
    @Select("SELECT * FROM product")
    List<Product> getProductM();

    @Select("SELECT * FROM product")
    List<Product> getProductS();
}
7.准备实现类
@Service
public class ProductService {
    Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    ProductDao productDao;
    public List<Product> findAllProductM() {
        List<Product> list = productDao.getProductM();
        logger.info("查询出来的信息,{}", list.toString());
        return list;
    }

    public List<Product> findAllProductS() {
        List<Product> list = productDao.getProductS();
        logger.info("查询出来的信息,{}", list.toString());
        return list;
    }
}
8.准备controller
@RestController
public class ProductController {
    @Autowired
    private ProductService productService;

    @GetMapping("/findAllProductM")
    public String findAllProductM() {
        String key = "master";
        RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);
        productService.findAllProductM();
        return "master";
    }
    
    @GetMapping("/findAllProductS")
     public String findAllProductS() {
        String key = "slave";
        RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);
        productService.findAllProductS();
        return "slave";
    }
}

9. 优化

(1)添加自定义注解

@Target(ElementType.METHOD)//该注解可以标注在方法上
@Retention(RetentionPolicy.RUNTIME)//设置生命周期
public @interface RoutingWith {
    String value() default "master";
}

(2)导入依赖

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

(3)编写切面类

@Aspect//切面类
@Component
public class RoutingAspect {

    @Around("@annotation(with)")//环绕通知,@annotation表示标注了某个注解的所有方法
    public Object routingWithDateSource(ProceedingJoinPoint joinPoint,RoutingWith with) throws Throwable {
        String key = with.value();
        RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);
        return joinPoint.proceed();
    }
}

(4)controller方法上添加自定义注解

@RestController
public class ProductController {
    @Autowired
    private ProductService productService;

    @RoutingWith("master")
    @GetMapping("/findAllProductM")
    public String findAllProductM() {
//        String key = "master";
//        RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);
        productService.findAllProductM();
        return "master";
    }

    @RoutingWith("slave")
    @GetMapping("/findAllProductS")
     public String findAllProductS() {
//        String key = "slave";
//        RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key);
        productService.findAllProductS();
        return "slave";
    }
}
10.项目结构

在这里插入图片描述

举报

相关推荐

0 条评论