赞
踩
目录
本篇主要介绍springboot如何进行多数据源配置,及一些设计思路。
主要包含以下内容:
1、多数据源通过注解动态切换
2、通过配置文件开关,只启用一个数据源
3、springboot中使用多数据源存在循环引用的问题
为了方便演示,在两台服务器上创建两个数据库,分别为master和slave
在master数据库创建表z_f_user,表结构及数据如上图。
在slave数据库创建表z_f_msg,表结构及数据如上图。
- <dependencies>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <scope>runtime</scope>
- </dependency>
- <!--druid -->
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid</artifactId>
- <version>1.2.11</version>
- </dependency>
- <dependency>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- <version>2.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- </dependency>
- </dependencies>
- #服务端口
- server.port=8080
- #服务名称
- spring.application.name=zhufeng-web-msg
-
- #数据源公共配置
- spring.datasource.druid.type=com.alibaba.druid.pool.DruidDataSource
- spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
- # master数据源配置
- spring.datasource.druid.dynamic.master.jdbc-url=jdbc:mysql://10.211.55.2:3306/master?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
- spring.datasource.druid.dynamic.master.username=root
- spring.datasource.druid.dynamic.master.password=123
- # slave数据源配置
- spring.datasource.druid.dynamic.slave.jdbc-url=jdbc:mysql://10.211.55.14:3306/slave?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
- spring.datasource.druid.dynamic.slave.username=root
- spring.datasource.druid.dynamic.slave.password=123
- #是否启动slave数据源开关,true:启用。默认为false
- spring.datasource.druid.dynamic.slave.enabled=true
-
- mybatis.type-aliases-package=com.zhufeng.web.mapper
- mybatis.mapper-locations=classpath:mapper/*.xml
- package com.zhufeng.base.db;
-
- import java.lang.annotation.*;
-
- /**
- * @ClassName: DataSource
- * @Description 数据源
- * @author 月夜烛峰
- * @date 2022/9/5 20:33
- */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface DataSource {
- /**
- *
- * 切换数据源名称
- */
- DataSourceType value() default DataSourceType.MASTER;
- }
- package com.zhufeng.base.db;
-
- /**
- * @ClassName: DataSourceType
- * @Description 数据源枚举值
- * @author 月夜烛峰
- * @date 2022/9/5 20:33
- */
- public enum DataSourceType {
- MASTER,
- SLAVE
- }
- package com.zhufeng.base.db;
-
- import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
- 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 org.springframework.context.annotation.Primary;
-
- import javax.sql.DataSource;
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * @ClassName: DataSourceConfig
- * @Description 配置多数据源bean,默认使用master数据源
- * @author 月夜烛峰
- * @date 2022/9/5 20:33
- */
- @Configuration
- public class DataSourceConfig {
- @Bean
- @ConfigurationProperties("spring.datasource.druid.dynamic.master")
- public DataSource masterDataSource() {
- return DataSourceBuilder.create().build();
- }
-
- @Bean
- @ConfigurationProperties("spring.datasource.druid.dynamic.slave")
- @ConditionalOnProperty(prefix = "spring.datasource.druid.dynamic.slave", name = "enabled", havingValue = "true")
- public DataSource slaveDataSource() {
- return DataSourceBuilder.create().build();
- }
-
- @Bean(name = "dynamicDataSource")
- @Primary
- public DynamicDataSource dataSource(DataSource masterDataSource,DataSource slaveDataSource) {
- Map<Object, Object> targetDataSources = new HashMap<>(8);
- targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
- targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
- return new DynamicDataSource(masterDataSource, targetDataSources);
- }
- }
- package com.zhufeng.base.db;
-
- 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.Order;
- import org.springframework.stereotype.Component;
-
- import java.lang.reflect.Method;
-
- /**
- * @ClassName: DataSourceAspect
- * @Description spring切层注入
- * @author 月夜烛峰
- * @date 2022/9/5 20:33
- */
- @Aspect
- @Order(1)
- @Component
- public class DataSourceAspect {
-
- @Pointcut("@annotation(com.zhufeng.base.db.DataSource)")
- public void dsPointCut() {
-
- }
-
- @Around("dsPointCut()")
- public Object around(ProceedingJoinPoint point) throws Throwable {
- MethodSignature signature = (MethodSignature) point.getSignature();
- Method method = signature.getMethod();
- DataSource dataSource = method.getAnnotation(DataSource.class);
- if (dataSource != null) {
- DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
- }
- try {
- return point.proceed();
- } finally {
- // 销毁数据源 在执行方法之后
- DynamicDataSourceContextHolder.clearDataSourceType();
- }
- }
- }
- package com.zhufeng.base.db;
-
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
-
- import javax.sql.DataSource;
- import java.util.Map;
-
- /**
- * @ClassName: DynamicDataSource
- * @Description 动态数据源创建
- * @author 月夜烛峰
- * @date 2022/9/5 20:33
- */
- public class DynamicDataSource extends AbstractRoutingDataSource {
-
- public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
- super.setDefaultTargetDataSource(defaultTargetDataSource);
- super.setTargetDataSources(targetDataSources);
- super.afterPropertiesSet();
- }
-
- /**
- * 根据Key获取数据源的信息
- *
- * @return
- */
- @Override
- protected Object determineCurrentLookupKey() {
- return DynamicDataSourceContextHolder.getDataSourceType();
- }
- }
- package com.zhufeng.base.db;
- /**
- * @ClassName: DynamicDataSourceContextHolder
- * @Description 数据源临时处理
- * @author 月夜烛峰
- * @date 2022/9/5 20:33
- */
- public class DynamicDataSourceContextHolder {
-
- /**
- * 使用ThreadLocal临时存储
- */
- private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
-
- /**
- * 设置数据源变量
- * @param dataSourceType
- */
- public static void setDataSourceType(String dataSourceType){
- CONTEXT_HOLDER.set(dataSourceType);
- }
-
- /**
- * 获取数据源变量
- * @return
- */
- public static String getDataSourceType(){
- return CONTEXT_HOLDER.get();
- }
-
- /**
- * 清空数据源变量
- */
- public static void clearDataSourceType(){
- CONTEXT_HOLDER.remove();
- }
- }
- package com.zhufeng.web.controller;
-
- import com.alibaba.fastjson.JSONObject;
- import com.zhufeng.base.db.DataSource;
- import com.zhufeng.base.db.DataSourceType;
- import com.zhufeng.web.service.MsgService;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.annotation.Resource;
-
- /**
- * @ClassName: DataController
- * @Description 多数据源切换演示
- * @author 月夜烛峰
- * @date 2022/9/5 20:40
- */
- @RestController
- @RequestMapping("data")
- public class DataController {
-
- @Resource
- MsgService msgService;
-
- @RequestMapping("master")
- public JSONObject showUser(){
- return msgService.showFirstUser();
- }
-
- @RequestMapping("slave")
- public JSONObject showMsg(){
- return msgService.showFirstMsg();
- }
- }
service接口:
- /**
- * @ClassName: MsgService
- * @Description TODO
- * @author 月夜烛峰
- * @date 2022/9/5 20:06
- */
- public interface MsgService {
- /**
- * 获取第一条消息信息
- * @return
- */
- JSONObject showFirstMsg();
-
- /**
- * 获取第一个用户信息
- * @return
- */
- JSONObject showFirstUser();
- }
实现类:
- package com.zhufeng.web.service.impl;
-
- import com.alibaba.fastjson.JSONObject;
- import com.zhufeng.base.db.DataSource;
- import com.zhufeng.base.db.DataSourceType;
- import com.zhufeng.web.mapper.MsgMapper;
- import com.zhufeng.web.service.MsgService;
- import org.springframework.stereotype.Service;
-
- import javax.annotation.Resource;
-
- /**
- * @ClassName: MsgServiceImpl
- * @Description TODO
- * @author 月夜烛峰
- * @date 2022/9/5 15:06
- */
- @Service
- public class MsgServiceImpl implements MsgService {
-
- @Resource
- MsgMapper msgMapper;
-
- /**
- * 获取第一条消息信息
- * @return
- */
- @DataSource(value = DataSourceType.SLAVE)
- @Override
- public JSONObject showFirstMsg() {
- return msgMapper.showFirstMsg();
- }
-
- /**
- * 获取第一个用户信息
- * @return
- */
- @Override
- public JSONObject showFirstUser() {
- return msgMapper.showFirstUser();
- }
- }
dao层代码:
- /**
- * @ClassName: MsgMapper
- * @Description TODO
- * @author 月夜烛峰
- * @date 2022/9/5 14:55
- */
- @Mapper
- public interface MsgMapper {
-
- /**
- * 获取第一条消息信息
- * @return
- */
- JSONObject showFirstMsg();
-
- /**
- * 获取第一个用户信息
- * @return
- */
- JSONObject showFirstUser();
- }
查询语句:
- <!--获取第一个用户信息-->
- <select id="showFirstUser" resultType="com.alibaba.fastjson.JSONObject">
- SELECT
- *
- FROM
- z_f_user
- LIMIT 0,1
- </select>
-
- <!--获取第一个消息信息-->
- <select id="showFirstMsg" resultType="com.alibaba.fastjson.JSONObject">
- SELECT
- *
- FROM
- z_f_msg
- LIMIT 0,1
- </select>
- /**
- * @ClassName: MsgApplication
- * @Description msg启动类
- * @author 月夜烛峰
- * @date 2022/7/22 08:53
- */
-
- @EnableDiscoveryClient
- @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
- public class MsgApplication {
- public static void main(String[] args) {
- SpringApplication.run(MsgApplication.class, args);
- }
- }
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})这里是启动时把DataSourceAutoConfiguration排除,避免在启动过程中出现循环引用问题
在application.properties中将以下配置删除或者改为false,重新启动项目
#是否启动slave数据源开关,true:启用。默认为false
spring.datasource.druid.dynamic.slave.enabled=false
再次访问slave,报错如下:
控制台报错信息:
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: SELECT * FROM z_f_msg LIMIT 0,1
### Cause: java.sql.SQLSyntaxErrorException: Table 'master.z_f_msg' doesn't exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'master.z_f_msg' doesn't exist] with root cause
错误日志显示,去master数据源中查询z_f_msg,也就是没有切换到slave数据源,在servce中添加的slave数据源的动态切换注解没有生效,使用默认的master数据源。
@DataSource(value = DataSourceType.SLAVE)
@Override
public JSONObject showFirstMsg() {
return msgMapper.showFirstMsg();
}
master作为默认数据源配置,在初始化数据源时已经指定
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。