SpringBoot 多数据源 指定数据源实现读写分离 手动实现和使用

引言

本篇文章讲一下如何实现多数据源实现读写分离,不涉及JTA微服务的分布式事务,如果需要了解分布式事务的可以在分布式专栏下找到相关文章

使用dynamic-datasource-spring-boot-starter

pom 依赖

 		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>

yaml 配置文件

server:
  port: 8080

# 建立公共配置 下面可以直接引入
druid-common: &druid-common #以下均为默认值
  initial-size: 10
  min-idle: 10
  max-active: 20
  max-wait: 10
  validation-query: SELECT 1
  validation-query-timeout: 30000
  test-on-borrow: true
  test-on-return: true
  test-while-idle: true
  keep-alive: true
  filters: stat,wall,log4j2

spring:
  datasource:
    dynamic:
      primary: slave # 默认用哪个数据源
      datasource:
        master:
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
          druid: #以下均为默认值
            <<: *druid-common
        slave:
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3307/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
          druid: #以下均为默认值
            <<: *druid-common
    druid:
      web-stat-filter:
        enabled: true
        url-pattern: /*
        exclusions: ["*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico"]
        session-stat-max-count: 1000
        session-stat-enable: true
        profile-enable: true
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        login-username: root
        login-password: root
        allow: 0.0.0.0
        reset-enable: true
      filter: # 如果不是使用log4j2请注释掉
        log4j2:
          enabled: true

使用@DS注解

注意:Service方法加上@DS注解
举个栗子:

	//默认使用从库查询
    public Account findAccountByName(String name) {
        return accountDao.findAccountByName(name);
    }

    @DS("master") //使用主库更新
    public int updateAccount(Account account) {
        return accountDao.updateAccount(account);
    }

手动实现理解原理

还是之前那个yaml配置文件,这里手动实现,我们需要实现

  • AbstractRoutingDataSource的实现类。
  • DS注解
  • AOP将对应的dataSourceKey设置到AbstractRoutingDataSource的实现类。

AbstractRoutingDataSource

AbstractRoutingDataSource是一个抽象类,当需要调用数据源时,getConnection()方法根据determineCurrentLookupKey返回的key(string类型)查找不同的目标数据源(bean id),通常我们会写一个ContextHolder,但是为了让代码看起来更直观,我们直接继承后省略掉ContextHolder

package com.doub1.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class RoutingDatasource extends AbstractRoutingDataSource {
	private static final ThreadLocal<String> holder = new ThreadLocal<String>();
	

	public static void setDataSourceKey(String dataSourceKey) {
        holder.set(dataSourceKey);
    }
 
 	public static string getDataSourceKey() {
        return holder.get();
    }
    
    //我们只需要实现这玩意即可
    @Override
    protected Object determineCurrentLookupKey() {
    	return getDataSourceKey();
        //return DataSourceContextHandler.getDataSource();
    }
}

实现@DS注解

package com.doub1.datasource;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DS {
   String value();
}

基于AOP给RoutingDatasource设置KEY

package com.doub1.datasource;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component("chooseDataSourceAspect")
public class ChooseDataSourceAspect {

	@Value("${spring.datasource.dynamic.primary}")
	private string primary;
	// 对service中的实现类找@DS注解,如果找到就设置对应的KEY
    @Before(value="execution(* com.doub1.service.impl..*.*(..))")
    public void before(JoinPoint point)
    {


        Object target = point.getTarget();
        String methodName = point.getSignature().getName();
        
		//懒得对接口检查了,如果需要自己写一下
        //Class<?>[] classz = target.getClass().getInterfaces();
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();

        try {
            Method m = target.getClass().getMethod(methodName, parameterTypes);
            if (m != null && m.isAnnotationPresent(DS.class)) {
                DS ds = m.getAnnotation(DS.class);
                RoutingDatasource.setDataSourceKey(ds.value());
            }else{
				RoutingDatasource.setDataSourceKey(primary);
			}
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

创建不同数据源

@Bean("master")
@ConfigurationProperties("spring.datasource.dynamic.datasource.master")
DruidDataSource master(){
	return DruidDataSource();
}

@Bean("slave")
@ConfigurationProperties("spring.datasource.dynamic.datasource.slave")
DruidDataSource slave(){
	return DruidDataSource();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

没事干写博客玩

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值