策略者模式之切换数据源应该这么做

目录

动态切换数据源原理:

动态切换数据源使用:


动态切换数据源原理:


        由于我们项目中由于数据量会越来越大,MySQL效率就会低下,于是我们做了动态的切换数据源操作,这样做是为了防止MySQL效率低下问题.
        首先定义一个子类继承AbstractRoutingDataSource数据源接口,当项目启动的时候,会加载系统中的多个数据源配置,当数据源加载成功之后,调用afterPropertiesSet方法把数据源加载到map集合中,准备数据源路由操作,然后定义自定义注解用于在执行sql的时候选择数据源,使用AOP的前置通知定义切面,在方法执行的时候把自定义注解中的key存储到ThreadLocal中,在真实调用的时候会调用determineCurrentLookupKey方法根据选择的key加载相关的数据源,这样就完成了动态数据源的切换。

废话不多说下面上代码:

动态切换数据源使用:

配置完整pom文件

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.10</version>
</parent>
<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.2.2</version>
        </dependency>


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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!--mybatis逆向工程插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
        </plugins>
    </build>

配置appcation.yml 文件:

server:
  port: 8080

spring:
  application:
    name: datasourcecheck
  datasource:
    ds1:
      url: jdbc:mysql://127.0.0.1:3306/ds1? useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 5
      minIdle: 5
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: [ stat,wall,log4j ]
      maxPoolPreparedStatementPerConnectionSize: 20
      useGlobalDataSourceStat: true
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    ds2:
      url: jdbc:mysql://127.0.0.1:3306/ds2? useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 5
      minIdle: 5
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: [ stat,wall,log4j ]
      maxPoolPreparedStatementPerConnectionSize: 20
      useGlobalDataSourceStat: true
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    #  切换Druid数据源
  type: com.alibaba.druid.pool.DruidDataSource

#  thymeleaf:
#    prefix: classpath:/aaa
#    suffix: .txt


mybatis:
  config-location: classpath:sqlMapConfig.xml
  mapper-locations: classpath:mapper/*.xml

 创建一个sqlMapConfig.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>

 启动类DatasourcecheckApp类:

@SpringBootApplication
@MapperScan("com.dk.mapper")
public class DatasourcecheckApp {

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

}

创建一个WR.java类这个类是做一个默认选择数据源作用的:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {
    String value()default "ds1";
}

 创建一个aop的包,包里创建一个WRaop.java的类,这里是使用Spring框架的AOP(面向切面编程)。WR注解标记的方法之前,从注解中获取一个值,并将这个值设置到某个数据源管理类中,可以用于动态切换数据源。。

@Component
@Aspect
public class WRaop {


    @Before("within(com.dk.service.impl.*)&&@annotation(wr)")
    public void before(JoinPoint joinPoint, WR wr){
        String value = wr.value();
        DyDatasource.t.set(value);
    }
}

 创建一个config的包,包下创建了两个配置类 DruidDatasourceConfig.java和DyDatasource .java类

@Configuration
public class DruidDatasourceConfig {

    @Bean("ds1")
    @ConfigurationProperties(prefix = "spring.datasource.ds1")
    public DataSource db1DataSource1() {
        return new DruidDataSource();
    }

    @Bean("ds2")
    @ConfigurationProperties(prefix = "spring.datasource.ds2")
    public DataSource db1DataSource2() {
        return new DruidDataSource();
    }

}

 DyDatasource .java类,这里它继承了AbstractRoutingDataSource类来创建一个动态数据源路由,实现在运行时根据请求的上下文动态地切换数据源。通常这种机制也可以用于读写分离、分库分表等场景.

@Component //Spring会自动检测并注册这个类为一个bean
@Primary    //表示当存在多个相同类型的bean时,这个bean将作为首选的bean。在有多个数据源配置的情况下,这个DyDatasource将被优先使用
public class DyDatasource extends AbstractRoutingDataSource {

    public static  ThreadLocal<String> t = new ThreadLocal<>();

    @Autowired
    private DataSource ds1;

    @Autowired
    private DataSource ds2;

    //返回数据源标识 通过扩展这个方法来实现数据源的切换
    @Override
    protected Object determineCurrentLookupKey() {
        return t.get();
    }

    //数据源加载完毕后立即存入map中,准备数据源路由的操作
    @Override
    public void afterPropertiesSet() {
        //设置数据源集合
        HashMap<Object, Object> map = new HashMap<>();
        map.put("ds1", ds1);
        map.put("ds2", ds2);
        super.setTargetDataSources(map);
        //设置默认数据源
        super.setDefaultTargetDataSource(ds1);

        super.afterPropertiesSet();
    }
}

 创建一个controller包,包下创建一个UserController .java类,注入DyDatasource .java类切换不同的数据源.

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private DyDatasource dyDatasource;
    @GetMapping("/select")
    public List<User> select(){
        //耦合度过高
        dyDatasource.t.set("ds1");
        List<User> users = userService.selectUser();
        return users;
    }



    @GetMapping("/insert")
    public String insertUser(){
        //耦合度过高
        dyDatasource.t.set("ds2");
        User user = new User();
        user.setUsername("aaa");
        user.setPassword("1234");
        userService.insertUser(user);
        return "ok";
    }

}

创建一个service包,包下创建UserService.java类

public interface UserService {
    List<User> selectUser();

    void insertUser(User user);
}

 创建一个service.impl包,包下创建UserServiceImpl.java类

@Service
public class UserServiceImpl implements UserService{

    @Autowired
    private UserMapper userMapper;
    
    @Override
    @WR("ds1")//使用哪个数据源就用这个注解引用;
    public List<User> selectUser() {
        return userMapper.selectByExample(null);
    }
    @Override
    @WR("ds2")
    public void insertUser(User user) {
        userMapper.insertSelective(user);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员_迪迦

你的鼓励是我创造最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值