SpringBoot+MyBatis使用AOP+注解实现多数据源

news/2024/11/19 15:19:07/

使用场景

一个应用程序需要链接多个数据库,比如读写分离架构下的读库和写库。

配置多数据源

数据库

创建数据库:ds1

CREATE TABLE `user`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

插入数据:

INSERT INTO `user` VALUES (1, 'ds1-1', 'ds1-1', 'ds1-1');
INSERT INTO `user` VALUES (2, 'ds1-2', 'ds1-2', 'ds1-2');
INSERT INTO `user` VALUES (3, 'ds1-3', 'ds1-3', 'ds1-3');

创建数据库:ds2

CREATE TABLE `user`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

插入数据:

INSERT INTO `user` VALUES (1, 'ds2-1', 'ds2-1', 'ds2-1');
INSERT INTO `user` VALUES (2, 'ds2-2', 'ds2-2', 'ds2-2');
INSERT INTO `user` VALUES (3, 'ds2-3', 'ds2-3', 'ds2-3');

添加项目依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.16</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.chaoyinsu</groupId><artifactId>multiple-data-sources</artifactId><version>0.0.1-SNAPSHOT</version><name>multiple-data-sources</name><description>multiple-data-sources</description><properties><java.version>1.8</java.version></properties><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.3.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></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>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter-test</artifactId><version>2.3.1</version><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

在YAML文件中定义数据源所需的数据

spring:datasource:mysql-datasource1:jdbc-url: jdbc:mysql://localhost:3306/ds1?useSSL=true&serverTimezone=Asia/Shanghaiusername: rootpassword: 666666driver-class-name: com.mysql.cj.jdbc.Drivermysql-datasource2:jdbc-url: jdbc:mysql://localhost:3306/ds2?useSSL=true&serverTimezone=Asia/Shanghaiusername: rootpassword: 666666driver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpath:/mapper/*.xml

定义多个数据源

package com.chaoyinsu.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;@Configuration
public class DataSourceConfig {@Bean(name = "mysqlDataSource1")@ConfigurationProperties(prefix = "spring.datasource.mysql-datasource1")public DataSource dataSource1(){return DataSourceBuilder.create().build();}@Bean(name = "mysqlDataSource2")@ConfigurationProperties(prefix = "spring.datasource.mysql-datasource2")public DataSource dataSource2(){return DataSourceBuilder.create().build();}
}

SpringBoot启动类

package com.chaoyinsu;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@MapperScan("com.chaoyinsu.mapper")
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})//禁用默认配置数据源
@EnableAspectJAutoProxy //开启Spring Boot对AOP的支持
public class MultipleDataSourcesApplication {public static void main(String[] args) {SpringApplication.run(MultipleDataSourcesApplication.class, args);}}

定义枚举来表示数据源的标识

public enum DataSourceType {MYSQL_DATASOURCE1,MYSQL_DATASOURCE2,}

继承AbstractRoutingDataSource类

package com.chaoyinsu.config;import com.chaoyinsu.common.DataSourceType;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@Component
@Primary
public class DataSourceManagement extends AbstractRoutingDataSource {//使用ThreadLocal而不是String,可以在多线程的时候保证数据的可靠性public static ThreadLocal<String> flag = new ThreadLocal<>();@Resourceprivate DataSource mysqlDataSource1; // 注入第一个数据源@Resourceprivate DataSource mysqlDataSource2; // 注入第二个数据源public DataSourceManagement(){ // 使用构造方法初始化ThreadLocal的值flag.set(DataSourceType.MYSQL_DATASOURCE1.name());}@Overrideprotected Object determineCurrentLookupKey() {return flag.get();}@Overridepublic void afterPropertiesSet() {Map<Object,Object> targetDataSource = new ConcurrentHashMap<>();targetDataSource.put(DataSourceType.MYSQL_DATASOURCE1.name(),mysqlDataSource1);targetDataSource.put(DataSourceType.MYSQL_DATASOURCE2.name(),mysqlDataSource2);super.setTargetDataSources(targetDataSource);super.setDefaultTargetDataSource(mysqlDataSource1);super.afterPropertiesSet();}
}

自定义注解

package com.chaoyinsu.common;import java.lang.annotation.*;@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {DataSourceType value() default DataSourceType.MYSQL_DATASOURCE1;
}

定义注解的实现类

package com.chaoyinsu.common;import com.chaoyinsu.config.DataSourceManagement;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Component
@Aspect
@Slf4j
public class TargetDataSourceAspect {@Before("@within(TargetDataSource) || @annotation(TargetDataSource)")public void beforeNoticeUpdateDataSource(JoinPoint joinPoint){TargetDataSource annotation = null;Class<? extends Object> target = joinPoint.getTarget().getClass();if(target.isAnnotationPresent(TargetDataSource.class)){// 判断类上是否标注着注解annotation = target.getAnnotation(TargetDataSource.class);log.info("类上标注了注解");}else{Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();if(method.isAnnotationPresent(TargetDataSource.class)){// 判断方法上是否标注着注解,如果类和方法上都没有标注,则报错annotation = method.getAnnotation(TargetDataSource.class);log.info("方法上标注了注解");}else{throw new RuntimeException("@TargetDataSource注解只能用于类或者方法上, 错误出现在:[" +target.toString() +" " + method.toString() + "];");}}// 切换数据源DataSourceManagement.flag.set(annotation.value().name());}}

实体类

package com.chaoyinsu.entity;import lombok.Data;/*** @author guochao* @version 1.0* @date 2023/10/17*/
@Data
public class User {private Long id;private String username;private String password;private String nickname;
}

UserService

package com.chaoyinsu.service;import com.chaoyinsu.entity.User;
import com.chaoyinsu.mapper.UserMapper;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;/*** @author guochao* @version 1.0* @date 2023/10/17*/
@Service
public class UserService {@Resourceprivate UserMapper userMapper;public List<User> list() {return userMapper.list();}
}

UserMapper接口

package com.chaoyinsu.mapper;import com.chaoyinsu.entity.User;import java.util.List;/*** @author guochao* @version 1.0* @date 2023/10/17*/
public interface UserMapper {List<User> list();
}

UserMapper.xml

<?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.chaoyinsu.mapper.UserMapper" ><select id="list" resultType="com.chaoyinsu.entity.User">select * from `user`</select></mapper>

使用

package com.chaoyinsu.controller;// 访问第一个数据库的t_user表import com.chaoyinsu.common.DataSourceType;
import com.chaoyinsu.common.TargetDataSource;
import com.chaoyinsu.config.DataSourceManagement;
import com.chaoyinsu.entity.User;
import com.chaoyinsu.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.List;@RestController
public class UserController {@Resourceprivate UserService userService;@GetMapping(value = "/user_list")public List<User> showUserList(){System.out.println(DataSourceType.MYSQL_DATASOURCE1.name());List<User> list = userService.list();return list;}@GetMapping(value = "/user_list2")// 将注解标注在方法上,表示此方法使用数据源2@TargetDataSource(value = DataSourceType.MYSQL_DATASOURCE2)public List<User> showUserList2(){List<User> list = userService.list();return list;}}

http://www.ppmy.cn/news/1158458.html

相关文章

数据库系统概论学习 1 绪论

1.1.1 数据、数据库、数据库管理系统、数据库系统 一、数据 Data 数据是数据库中存储的基本对象 定义&#xff1a;描述事物的符号记录称为数据&#xff0c;描述事物的符号可以是数字、文字、图像、图形、声音、语言等表现形式&#xff0c;它们都可以经过数字化后存入计算机。…

不写注释就是耍流氓?

不写注释就是耍流氓&#xff1f; 关于写代码不写注释这么说“我”不想写注释的原因如何才能写出漂亮的注释 关于写代码不写注释这么说 关于代码注释的争论一直存在&#xff0c;程序员社区中有不同的观点和实践。写代码时是否应该写注释是一个有深度的话题&#xff0c;我认为需…

CSS之实现线性渐变背景

1. background: linear-gradient() background: linear-gradient是CSS中用于创建线性渐变背景的属性&#xff0c;这个属性允许你定义一个在元素的背景中进行渐变的效果&#xff0c;可以从一个颜色过渡到另一个颜色。 基本语法 background: linear-gradient(direction, color-…

mysql按指定字符截取

1、使用SUBSTRING函数进行截取 语法&#xff1a;str是要截取的字符串&#xff0c;pos是起始位置&#xff0c;len是要截取的长度 SUBSTRING(str, pos, len)例子 SELECT SUBSTRING(Hello, World!, 1, 5);返回"Hello"。其中&#xff0c;起始位置为1&#xff0c;截取的…

倾斜摄影三维模型的根节点合并的重要性分析

倾斜摄影三维模型的根节点合并的重要性分析 倾斜摄影三维模型的根节点合并是整个模型构建过程中的一个重要环节&#xff0c;具有重要的意义和作用。本文将对倾斜摄影三维模型的根节点合并的重要性进行详细分析。 一、定义和概述 在倾斜摄影三维模型的构建过程中&#xff0c;根…

window10右键新增md文档

桌面新建txt文本 输入下列信息: Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\.md] "markdown"[HKEY_CLASSES_ROOT\.md\ShellNew] "NullFile"""[HKEY_CLASSES_ROOT\markdown] "Markdown 标记文档"修改拓展名为reg保存,双…

Junit 集成测试

前言 现在作者说明一下&#xff0c;作者需要开发一个简单的VueSpringboot前后端分离实验&#xff0c;想要尽量将测试的流程应用到这样的系统中。单元测试请见Junit单元测试_Joy T的博客-CSDN博客&#xff0c;而单元测试加上mock呢&#xff0c;最多也只能测试一下Service层的业…

微信群发消息怎么发?群发消息,只要这4个步骤!

微信是我们日常生活中使用最广泛的社交软件之一。用户通过微信可以向好友、家人、同事等联系人发送文字、图片、视频、语音、文件等信息&#xff0c;是一款非常实用的即时通信应用程序。 除了与好友进行单独聊天&#xff0c;我们有时候可能也需要将信息进行群发。但是还有很多…