Springboot+mybatis+mysql+注解+AOP配置多源数据库

Springboot+mybatis+mysql+注解+AOP配置多源数据库

整个项目代码在Q群:903233760

1.创建springboot项目

2.向pom.xml导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!--<version>2.5.4</version>-->
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--<version>8.0.26</version>-->
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <!--<version>2.5.4</version>-->
</dependency>

3.创建数据库表

CREATE DATABASE /*!32312 IF NOT EXISTS*/`test1` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `test1`;
/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `name` varchar(64) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `user` */
insert  into `user`(`name`) values ('李四');
CREATE DATABASE /*!32312 IF NOT EXISTS*/`test2` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `test2`;
/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `name` varchar(40) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `user` */
insert  into `user`(`name`) values ('张三');

请添加图片描述
4.在spring boot配置文件(application.yml)中配置连接的数据库信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBnUKn5s-1630114840155)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828081058289.png)]

spring:
    datasource:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      druid:
        master:
          url: jdbc:mysql://localhost:3306/test1?serverTimezone=UTC
          username: root
          password: root
        slave:
          enabled: true
          url: jdbc:mysql://localhost:3306/test2?serverTimezone=UTC
          username: root
          password: root

5.创建DruidConfig配置类

在配置类中配置多个Datasource
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vzL1qXT-1630114840157)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828081608099.png)]

@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")

这个注解的意思是当spring.datasource.druid.slave.enabled==true时,才创建相应的DataSource加入到spring容器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-waEGLt1k-1630114840159)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828081758828.png)]
将刚才创建的多个DataSource存放在一起方便以后的切换和注入这里默认masterDataSource为主数据源,将主数据源和所有数据源传给DynamicDataSource类

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

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

@Configuration
public class DruidConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(){
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return dataSource;
    }
    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(){
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return dataSource;
    }


    @Bean(name="dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource,DataSource slaveDataSource){
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(),masterDataSource);
        targetDataSources.put(DataSourceType.SLAVE.name(),slaveDataSource);
        return new DynamicDataSource(masterDataSource,targetDataSources);
    }
}

6.创建DynamicDataSource继承AbstractRoutingDataSource类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hm3sUgGi-1630114840160)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828082857118.png)]
DynamicDataSource类继承了AbstractRoutingDataSource类实现动态数据源切换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i6s3NmZS-1630114840162)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828084031760.png)]
在DynamicDataSource构造方法中调用了父类setDefaultTargetDataSource方法把主数据源中的信息赋值给父类的defaultTargetDataSource属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMyxUERS-1630114840162)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828084207207.png)]
调用了父类setTargetDataSources方法把所有数据源中的信息赋值给父类的targetDataSources属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ChZ9pBLB-1630114840163)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828083525938.png)]
最后调用了父类afterPropertiesSet方法。在父类的afterPropertiesSet方法中,可以观察到他将我们所设置给父的所有数据源信息,转移到父类的resolvedDataSources属性中,并将我们传给父类的主数据源信息递给父类的resolvedDefaultDataSource属性
DynamicDataSource类还实现了父类的(AbstractRoutingDataSource类)determineCurrentLookupKey方法 这个方法返回值是我们决定切换的数据源是哪个。
在父类中determineTargetDataSource方法调用了我们在子类中实现的determineCurrentLookupKey方法,获取到我们索要切换的数据库类型,在从所有数据源中获取到DataSource并返回,如果我们切换的数据源类型没有就返回我们的默认的主数据源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uQpSIchw-1630114840164)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828085840837.png)]
因为在我们查数据库的时候每次都要获取数据库连接都会调用这个类的determineTargetDataSource方法这个方法决定了我们的数据源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xg9r3v8Z-1630114840165)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828090054596.png)]

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
 * 动态数据源
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    public DynamicDataSource(DataSource defaultTargetDataSource , Map<Object,Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
    @Override //决定现在的
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

7.实现DynamicDataSourceContextHolder类
实现了一个DynamicDataSourceContextHolder用来存放我们要切换数据源的类型,这里有注解不多说了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TN5auYZN-1630114840165)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828090533110.png)]

public class DynamicDataSourceContextHolder {
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType)
    {
        CONTEXT_HOLDER.set(dsType);
    }
    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get();
    }
    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

8.实现DataSourceType数据库类型枚举类

public enum DataSourceType {
    MASTER,
    SLAVE
}

9.创建User、UserMapper、UserMapper.xml类

public class User {
    private String name;
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface UserMapper {
    public List<User> selectList2();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.UserMapper">

    <select id="selectList2" resultType="com.example.User">
        select * from user
  </select>
</mapper>

10.在pom文件中指定Mapper文件资源的位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pw2QfcWt-1630114840166)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828091133336.png)]

<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
    </resource>
</resources>

11.创建DataSource注解类

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切换数据源名称
     */
    public DataSourceType value() default DataSourceType.MASTER;
}

12.创建DataSourceAspect,对使用注解的方法做切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 多数据源处理
 * 
 * @author ruoyi
 */
@Aspect
@Component
public class DataSourceAspect
{

    @Pointcut("@annotation(com.example.DataSource)"
            + "|| @within(com.example.DataSource)")
    public void dsPointCut()
    {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        DataSource dataSource = getDataSource(point);

        if (dataSource!=null)
        {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }
        try
        {
            return point.proceed();
        }
        finally
        {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
    /**
     * 获取需要切换的数据源
     */
    public DataSource getDataSource(ProceedingJoinPoint point)
    {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource))
        {
            return dataSource;
        }

        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}

13.创建UserService类,从两个不同的数据源中查询信息

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    @Autowired
    UserMapper userMapper;

    @DataSource(value=DataSourceType.SLAVE)
    public List<User> selectAllUser(){
        return userMapper.selectList2();
    }
    @DataSource(value=DataSourceType.MASTER)
    public List<User> selectAllUser2(){
        return userMapper.selectList2();
    }
}

14.创建Controller类,将查询的信息输出到控制台

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {

    @Autowired
    UserService userService;
    @GetMapping("/test")
    public void test(){
        System.out.println("数据源1查询的数据:"+userService.selectAllUser());
        System.out.println("数据源2查询的数据:"+userService.selectAllUser2());
    }
}

15.启动springboot应用 打开浏览器访问http://localhost:8080/test会看到控制台输出一下信息,说明成功了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfbxAvcR-1630114840167)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828091726780.png)]
16.项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EBCDxbfZ-1630114840168)(C:\Users\Administrator\Desktop\配置多源数据库\image-20210828091823886.png)]
17.总结跟着Ruoyi大佬学到了很多东西,对于刚走上开发的我来说启发很大

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值