SpringBoot多数据源配置并通过注解实现动态切换数据源
1. 环境准备
1.1 数据库准备
一个本地环境的MySQL数据库,数据库mydb,创建表t_user
CREATE TABLE `t_user` (
`c_id` varchar(20) NOT NULL,
`c_username` varchar(20) DEFAULT NULL,
`c_password` varchar(20) DEFAULT NULL,
`c_gender` tinyint(2) DEFAULT NULL,
PRIMARY KEY (`c_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `mydb`.`t_user`(`c_id`, `c_username`, `c_password`, `c_gender`) VALUES ('1', '思思', '123', 1);
一个云服务器的MySQL数据库,创建数据库book_db,创建表t_userinfo。
CREATE TABLE `t_user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
`password` varchar(255) DEFAULT NULL COMMENT '登录密码',
`areaObj` varchar(255) DEFAULT NULL COMMENT '所在学院',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
`sex` tinyint(255) DEFAULT NULL COMMENT '性别',
`user_photo` varchar(255) DEFAULT NULL COMMENT '学生照片',
`birthday` varchar(20) DEFAULT NULL COMMENT '出生日期',
`telephone` varchar(20) DEFAULT NULL COMMENT '联系电话',
`address` varchar(255) DEFAULT NULL COMMENT '家庭地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `book_db`.`t_user_info`(`id`, `user_name`, `password`, `areaObj`, `name`, `sex`, `user_photo`, `birthday`, `telephone`, `address`) VALUES (1, '张三', '123', '哈尔滨', '张三散', 1, '123', '02-16', '15756892458', '黑龙江省哈尔滨市');
创建数据库chatroom,创建表admin。
CREATE TABLE `admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL COMMENT '登录账号',
`nickname` varchar(20) NOT NULL COMMENT '昵称',
`password` varchar(255) NOT NULL COMMENT '密码',
`user_profile` varchar(255) DEFAULT NULL COMMENT '管理员头像',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
INSERT INTO `chatroom`.`admin`(`id`, `username`, `nickname`, `password`, `user_profile`) VALUES (1, 'admin', '系统管理员', '$2a$10$PyloUEVGuO0fUZdfeIaROOTluRmccl.Scifa8S7Os0Wt.s4bDkb', 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1784117537,3335593911&fm=26&gp=0.jpg');
1.2 项目创建
创建SpringBoot项目,整合MyBatis-Plus。pom.xml引入的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
配置读取resource文件夹下的mapper文件
<build>
<resources>
<resource>
<directory>class="lazy" data-src/main/java</directory>
<includes>
<include>**
CHATROOM("chatroom"),
BOOK_DB("book_db"),
MY_DB("mydb");
private final String name;
DataSourceTypeEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
4.2 创建动态多数据源类
定义一个动态多数据源类DynamicDataSource用于管理不同线程间多个数据源的选择和切换,扩展 Spring 提供的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法,其中的determineCurrentLookupKey() 方法用于决定使用哪个数据源。
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
4.3 创建动态多数据源配置类
DynamicDataSourceConfig类作为配置类,读取配置文件的三个数据源的配置,创建对应DataSource类型的Bean。
@Configuration
public class DynamicDataSourceConfig {
@Bean(name="chatroom")
@ConfigurationProperties("spring.datasource.druid.first")
public DataSource dataSource1(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name ="book_db")
@ConfigurationProperties("spring.datasource.druid.second")
public DataSource dataSource2(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name="mydb")
@ConfigurationProperties("spring.datasource.druid.third")
public DataSource dataSource3(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name="dynamicDataSource")
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>(5);
targetDataSources.put(DataSourceTypeEnum.CHATROOM.getName(), dataSource1());
targetDataSources.put(DataSourceTypeEnum.BOOK_DB.getName(), dataSource2());
targetDataSources.put(DataSourceTypeEnum.MY_DB.getName(), dataSource3());
return new DynamicDataSource(dataSource1(), targetDataSources);
}
}
4.4 自定义注解用于指定数据源
自定义注解@SpecifyDataSource用于在Service层方法上标记要使用哪个数据源。这里定义默认使用数据源 DataSourceType.CHATROOM。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpecifyDataSource {
DataSourceTypeEnum value() default DataSourceTypeEnum.CHATROOM;
}
4.5 AOP实现动态切换数据源
定义数据源界面类DataSourceAspect,用于实现有SpecifyDataSource注解标注的方法前切换注解指定的数据源。
@Aspect
@Component
@Order(value = 1)
public class DataSourceAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Pointcut("@annotation(top.javahai.datasource.annotation.SpecifyDataSource)")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
SpecifyDataSource ds = method.getAnnotation(SpecifyDataSource.class);
if (ds == null) {
DynamicDataSource.setDataSource(DataSourceType.CHATROOM.getName());
logger.info("set datasource is " + DataSourceType.CHATROOM);
} else {
DynamicDataSource.setDataSource(ds.value().getName());
logger.info("set datasource is " + ds.value().getName());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
logger.info("clean datasource");
}
}
}
5. 测试使用
5.1 配置数据源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
# 数据源1
spring.datasource.druid.first.url=jdbc:mysql://158.156.444.68:3306/chatroom?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.first.username=root
spring.datasource.druid.first.password=123456
# 数据源2
spring.datasource.druid.second.url=jdbc:mysql://158.156.444.68:3306/book_db?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.second.username=root
spring.datasource.druid.second.password=123456
#数据源3
spring.datasource.druid.third.url=jdbc:mysql:///mydb?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.third.username=root
spring.datasource.druid.third.password=123456
mybatis-plus.mapper-locations=classpath:mapper
private String username;
private String nickname;
private String password;
private String userProfile;
//省略getter/setter方法
创建实体类TUser
public class TUser implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "c_id", type = IdType.AUTO)
private Integer cId;
private String cUsername;
private String cPassword;
private Integer cGender;
}
创建实体类TUserinfo
@TableName(value = "t_user_info")
public class TUserinfo implements Serializable {
private static final long serialVersionUID = 1L;
private String userName;
private String password;
@TableField("areaObj")
private String areaObj;
private String name;
private Integer sex;
private String userPhoto;
private String birthday;
private String telephone;
private String address;
}
创建UserVO用于测试
public class UserVO {
private List<Admin> adminList;
private List<TUserinfo> tUserinfos;
private List<TUser> tUsers;
}
5.3 服务层代码
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {
public List<Admin> getAll(){
return this.list(null);
}
}
@Service
public class TUserinfoServiceImpl extends ServiceImpl<TUserinfoMapper, TUserinfo> implements ITUserinfoService {
@SpecifyDataSource(value = DataSourceTypeEnum.BOOK_DB)
public List<TUserinfo> selectAll(){
return this.list(null);
}
}
@Service
public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements ITUserService {
@SpecifyDataSource(value = DataSourceTypeEnum.MY_DB)
public List<TUser> selectAll(){
return this.list(null);
}
}
public interface AdminMapper extends BaseMapper<Admin> {
}
public interface TUserinfoMapper extends BaseMapper<TUserinfo> {
}
public interface TUserMapper extends BaseMapper<TUser> {
}
5.4 控制层代码
创建接口/test/list用于测试
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private AdminServiceImpl adminService;
@Autowired
private TUserinfoServiceImpl userinfoService;
@Autowired
private TUserServiceImpl userService;
@GetMapping("/list")
public UserVO list(){
List<Admin> adminList= adminService.getAll();
List<TUserinfo> tUserinfos = userinfoService.selectAll();
List<TUser> tUsers = userService.selectAll();
UserVO userVO = new UserVO();
userVO.setAdminList(adminList);
userVO.settUserinfos(tUserinfos);
userVO.settUsers(tUsers);
return userVO;
}
}
浏览器请求/test/list
查看控制台输出,查看数据源的切换日志
完整Demo代码地址:https://github.com/JustCoding-Hai/learn-everyday/tree/master/learn-multi_data_source
到此这篇关于SpringBoot多数据源配置并通过注解实现动态切换数据源的文章就介绍到这了,更多相关SpringBoot 动态切换数据源内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341