百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

一、SpringBoo中Mybatis多数据源动态切换

ztj100 2025-02-20 18:56 25 浏览 0 评论


我们以一个实例来详细说明一下如何在SpringBoot中动态切换MyBatis的数据源。


一、需求

1、用户可以界面新增数据源相关信息,提交后,保存到数据库

2、保存后的数据源需要动态生效,并且可以由用户动态切换选择使用哪个数据源

3、数据库保存了多个数据源的相关记录后,要求在系统启动时把这些个数据源创建出来,用户在使用时可以自由选择切换

二、项目准备

创建项目的基础骨架

建项目

项目名:dds

改pom



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        3.3.5
         
    
    com.xiaoxie
    dds
    0.0.1-SNAPSHOT
    dds
    dds
    
        17
    
    
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            3.0.3
        

        
            com.mysql
            mysql-connector-j
            runtime
        

        
            com.alibaba
            druid-spring-boot-starter
            1.2.8
        

        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter-test
            3.0.3
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    


修改yml


server:
  port: 8888

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/dds?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
      username: root
      password: root


mybatis:
  mapper-locations: classpath:mapper/**.xml
  configuration:
    map-underscore-to-camel-case: true

主启动类


@SpringBootApplication
public class DdsApplication {

    public static void main(String[] args) {
        SpringApplication.run(DdsApplication.class, args);
    }

}

做完成上面就是不带任何业务类的一个基础项目框架。


数据库准备


新增一个数据库dds,其中有两个数据表,一个是用来存储用户提交的数据源信息的(ds),一个是后续我们测试效果用的(test)。


CREATE TABLE `ds` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '数据源名称',
  `url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'url',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'username',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'password',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;



CREATE TABLE `test` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

INSERT INTO `dds`.`test` (`id`, `name`) VALUES (1, '王二麻子');

新增一个测试库test,其中有一个测试数据表,这个表的结构保持与dds库中的test表一致,但数据不一样。

CREATE TABLE `test` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `test`.`test` (`id`, `name`) VALUES (1, '张三');

三、处理与前端的交互

后端接口

新增一个Controller类,这个类中添加一个处理器方法:AddSourceController

@Controller
@Slf4j
@RequiredArgsConstructor
public class AddSourceController {

    private final DsService dsService;

    @GetMapping("/toAddSource")
    public String addSource(){
        return "add_source";
    }
}

这样的话当我们请求项目的/toAddSource接口时,跳转到add_source.html,在这个页面中我们进行用户数据的提交动作。

前端页面

html




    
    添加数据源
    
    
    
    
    


添加数据源(MySQL)

css

页面css文件,js文件如下:style.css

body {
    font-family: Arial, sans-serif;
    margin: 20px;
}

.form-group {
    margin-bottom: 15px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input[type="text"], input[type="password"] {
    width: 100%;
    padding: 8px;
    box-sizing: border-box;
}

button {
    padding: 10px 20px;
    margin-right: 10px;
}

.error {
    color: red;
    font-size: 0.9em;
}

.valid-icon {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: green;
    /*display: none;*/
}
.input-container {
    position: relative;
    display: inline-block;
    width: 100%;
}
.input-container input {
    padding-right: 25px; /* 为对勾图标留出空间 */
}

js

const secretKey = CryptoJS.enc.Latin1.parse("aab11e66fa232e32");
const iv = CryptoJS.enc.Latin1.parse("aab11e66fa232e32");
let isValid = true;


/**
 * 测试连通性
 */
function testConnection() {
    const dataSourceName = $('#dataSourceName').val().trim();
    const url = $('#url').val().trim();
    const username = $('#username').val().trim();
    const password = $('#password').val().trim();
    checkDataSourceName();
    checkUrl();
    if (username === '') {
        $('#usernameError').text('连接账号不能为空');
        isValid = false;
    } else {
        $('#usernameError').text('');
    }

    if (password === '') {
        $('#passwordError').text('连接密码不能为空');
        isValid = false;
    } else {
        $('#passwordError').text('');
    }
    if(isValid) {
        // 禁用提交按钮
        $('#dataSourceForm button[type="submit"]').prop('disabled', true);

        let encryptedPassword = CryptoJS.AES.encrypt(password, secretKey, {iv: iv, mode:CryptoJS.mode.CBC,
        padding:CryptoJS.pad.ZeroPadding}).toString();
        const data = {
            name: dataSourceName,
            url: url,
            username: username,
            password: encryptedPassword
        }
        // 向后端发送ajax请求
        $.ajax({
            url: '/testConnection',
            type: 'POST',
            data: JSON.stringify(data),
            contentType: 'application/json',
            success: function(response) {
                if(response.code === 200) {
                    alert('测试连接成功!');
                } else {
                    alert('测试连接失败!');
                }
            },
            error: function() {
                alert('测试连接时发生错误!');
            },
            complete: function() {
                // 无论成功还是失败,都重新启用提交按钮
                $('#dataSourceForm button[type="submit"]').prop('disabled', false);
            }
        });
    }
}

/**
 *  校验数据源名称是否已经在数据库中记录表中已存在,如果已经存在,则提示用户,否则继续执行。
 */
function checkDataSourceName() {
    const dataSourceName = $('#dataSourceName').val().trim();
    if (dataSourceName === '') {
        $('#dataSourceNameError').text('数据源名称不能为空!')
        isValid = false;
    }
    /* 向后端发送ajax请求,密码是否已经存在 */
    $.ajax({
        url: '/checkDataSourceName',
        type: 'POST',
        data: JSON.stringify({dataSourceName: dataSourceName}),
        contentType: 'application/json',
        success: function(response) {
            if(response.code === 200) {
                if(response.msg === '存在') {
                    $('#dataSourceNameError').text('数据源名称已存在,不可重复哦!');
                    $('#sourceNameValid').hide(); // 隐藏对勾图标
                    isValid = false;
                } else if(response.msg === '不存在') {
                    $('#dataSourceNameError').text('');
                    $('#sourceNameValid').show(); // 显示对勾图标
                    isValid = true;
                } else {
                    $('#dataSourceNameError').text('检查数据源名称时发生错误!')
                    $('#sourceNameValid').hide(); // 隐藏对勾图标
                    isValid = false;
                }
            } else {
                $('#dataSourceNameError').text('检查数据源名称时发生错误!')
                $('#sourceNameValid').hide(); // 隐藏对勾图标
                isValid = false;
            }
        },
        error: function() {
            $('#dataSourceNameError').text('检查数据源名称时发生错误!')
            $('#sourceNameValid').hide(); // 隐藏对勾图标
            isValid = false;
        }
    })
}

/**
  * 检查输入的url是否满足mysql连接字符串的规则
 */
function checkUrl() {
    const url = $('#url').val().trim();
    if (url === '') {
        $('#urlError').text('URL连接字符串不能为空');
    }
    const urlRegex = /^jdbc:mysql:\/\/[a-zA-Z0-9.-]+(:\d+)?(\/[a-zA-Z0-9._-]+)?(\?[^=]+=.*)?$/;
    if(!urlRegex.test(url)) {
        $('#urlError').text('URL连接字符串格式不正确,请重新输入');
        isValid = false;
    } else {
        $('#urlError').text('');
        $('#urlValid').show();
        isValid = true;
    }
}


/**
 * 提交数据源数据
 */
function submitForm() {
    const dataSourceName = $('#dataSourceName').val().trim();
    const url = $('#url').val().trim();
    const username = $('#username').val().trim();
    const password = $('#password').val().trim();
    if (checkDataSourceName() && checkUrl()) {
        isValid = true;
    }
    if (username === '') {
        $('#usernameError').text('连接账号不能为空');
        isValid = false;
    } else {
        $('#usernameError').text('');
    }

    if (password === '') {
        $('#passwordError').text('连接密码不能为空');
        isValid = false;
    } else {
        $('#passwordError').text('');
    }

    if(isValid) {
        // 防止表单多次重复提交
        $('#dataSourceForm button[type="submit"]').prop('disabled', true);
        let encryptedPassword = CryptoJS.AES.encrypt(password, secretKey, {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding}).toString();
        const data = {
            name: dataSourceName,
            url: url,
            username: username,
            password: encryptedPassword
        };
        $.ajax({
            url: '/addDataSource',
            type: 'POST',
            data: JSON.stringify(data),
            contentType: 'application/json',
            success: function(response) {
                if(response.code === 200) {
                    alert("添加成功!")
                } else {
                    alert("添加失败!")
                }
            },
            error: function() {
                alert("添加时发生错误!")
            },
            complete: function() {
                // 无论成功还是失败,都重新启用提交按钮
                $('#dataSourceForm button[type="submit"]').prop('disabled', false);
            }

        });
    }
}

这个js要注意一下我们使用了CryptoJS,对于前端传到后端的数据库连接密码进行加密,其中需要有key和iv,我们当前设置为:aab11e66fa232e32,后端到时解密时也需要使用相同的值,所以我们把这个值配置到后端项目的application.yml当中

crypto:
  secret: aab11e66fa232e32

四、后端处理接口

在上面js当中有多个地方需要使用ajax调用后端的接口,所以后端要完善这些接口,在完善它们之前我们要在后端要把实体类、统一的json返回这些基础搭建好。

实体类

Ds

@Data
public class Ds implements Serializable {
    /**
     * 主键
     */
    private Long id;

    /**
     * 数据源名称
     */
    private String name;

    /**
     * url
     */
    private String url;

    /**
     * username
     */
    private String username;

    /**
     * password
     */
    private String password;

    /**
     * 创建时间
     */
    private Date createTime;

    private static final long serialVersionUID = 1L;
}

Test

@Data
public class Test {
    private Long id;
    private String name;
}

DsDto

@Data
public class DsDto {
    private String name;
    private String url;
    private String username;
    private String password;
}

统一返回

ReturnCode枚举

public enum ReturnCode {
    SUCCESS(200, "成功"),
    FAIL(400, "失败"),
    INTERNAL_SERVER_ERROR(500, "服务器内部错误");

    private Integer code;
    private String msg;

    ReturnCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

ResultData

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultData {

    private Integer code;
    private String msg;
    private Object data;


    public static ResultData ok(Object data) {
        return new ResultData(ReturnCode.SUCCESS.getCode(), ReturnCode.SUCCESS.getMsg(), data);
    }

    public static ResultData ok(String msg) {
        return new ResultData(ReturnCode.SUCCESS.getCode(), msg, null);
    }

    public static ResultData ok(String msg, Object data) {
        return new ResultData(ReturnCode.SUCCESS.getCode(), msg, data);
    }

    public static ResultData ok(ReturnCode returnCode) {
        return new ResultData(returnCode.getCode(), returnCode.getMsg(), null);
    }

    public static ResultData ok(ReturnCode returnCode,Object data) {
        return new ResultData(returnCode.getCode(), returnCode.getMsg(), data);
    }

    public static ResultData fail(Object data) {
        return new ResultData(ReturnCode.FAIL.getCode(), ReturnCode.FAIL.getMsg(), data);
    }

    public static ResultData fail(String msg) {
        return new ResultData(ReturnCode.FAIL.getCode(), msg, null);
    }

    public static ResultData fail(Integer code,String msg) {
        return new ResultData(code, msg, null);
    }

    public static ResultData fail(String msg, Object data) {
        return new ResultData(ReturnCode.FAIL.getCode(), msg, data);
    }

    public static ResultData fail(ReturnCode returnCode) {
        return new ResultData(returnCode.getCode(), returnCode.getMsg(),null);
    }

    public static ResultData fail(ReturnCode returnCode,Object data) {
        return new ResultData(returnCode.getCode(), returnCode.getMsg(), data);
    }

    public static ResultData fail(ReturnCode returnCode,String msg) {
        return new ResultData(returnCode.getCode(), msg, null);
    }
}

相关接口

/checkDataSourceName

这个接口的目的是检测数据源的名称是否在数据库已经存在,我们动态切换数据源的时候是按这个名称来切换的,所以要求这里的名称要保持一致!

@PostMapping("/checkDataSourceName")
    @ResponseBody
    public ResultData checkDataSourceName(@RequestBody Map params){
        // dsService.switchDataSource("defaultDataSource");
        String name = params.get("dataSourceName");
        log.info("检查数据源名称开始:" + name);
        List dsList = dsService.selectByName(name);
        if (!dsList.isEmpty()){
            return ResultData.ok("存在");
        }
        return ResultData.ok("不存在");
    }

这里我们要调service,所以新增service接口及对应的接口

public interface DsService {

    List selectByName(String name);
}
@Service
@RequiredArgsConstructor
public class DsServiceImpl implements DsService {

   
    private final DsMapper dsMapper;

    @Override
    public List selectByName(String name) {
        return dsMapper.selectByName(name);
    }

}

service中需要依赖mybatis查询数据库,新增mapper接品及对应的sql映射文件

@Mapper
public interface DsMapper {

    List selectByName(@Param("name") String name);

    List selectAll();
}



  
    
    
    
    
    
    
  

  
    id, `name`, url, username, `password`, create_time
  
  
   

/testConnection

这个接口是获取用户提供的值,根据这些值我们来测试是否可以正常连上数据库

controller中新增方法

    @PostMapping("/testConnection")
    @ResponseBody
    public ResultData testConnection(@RequestBody DsDto dsDto) throws Exception {
        boolean result = dsService.testConnection(dsDto);
        return result ? ResultData.ok("连接成功") : ResultData.fail("连接失败");
    }

dsService中新增方法testConnection(),service接口和实现类如下:

boolean testConnection(DsDto dsDto) throws Exception;
    @Value("${crypto.secret}")
    private String secretKey;    

    @Override
    public boolean testConnection(DsDto dsDto) {
        try {
            String password = AESUtils.decrypt(dsDto.getPassword(), secretKey);
            return testConnectDB(dsDto.getUrl(), dsDto.getUsername(), password);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 测试数据库是否可连接
     * @param url
     * @param username
     * @param password
     * @return
     */
    private boolean testConnectDB(String url, String username, String password) {
        try{
            Connection connection = DriverManager.getConnection(url, username, password);
            return connection != null && !connection.isClosed();
        } catch (Exception e) {
            return false;
        }

    }

这里注意一下,我们从前端传过来的是密码是加过密的,所以对于dto中的密码我们是要进行解密的,所以我们提供一个工具类:AESUtils

public class AESUtils {
    public static String decrypt(String content, String key) throws Exception{
        try {
            byte[] encrypted1 = Base64.getDecoder().decode(content);
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(key.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            // 去掉补充的字符
            String originalString = new String(original).trim();
            return originalString;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

/addDataSource

用户在界面点击提交后,我们需要把数据库连接信息保存到数据库当中,使用这个接口来完成,这个接口除了保存这些信息外,还有一个本次最要的工作要做,就是所保存这个数据库连接相关的信息动态创建一个数据源,以便供后续可以动态切换使用!!

Controller中新增如下代码:

    @PostMapping("/addDataSource")
    @ResponseBody
    public ResultData addDataSource(@RequestBody DsDto dsDto){
        System.out.println(dsDto);
        boolean result = dsService.addDs(dsDto);
        return result ? ResultData.ok("添加成功") : ResultData.fail("添加失败");
    }

service接口及实现类

boolean addDs(DsDto dsDto);
    private final DynamicDataSourceManager dynamicDataSourceManager;
    private final Map dataSourceMap = new ConcurrentHashMap<>();    
    
    @Override
    @Transactional
    public boolean addDs(DsDto dsDto){
        //1、把数据库连接信息保存到数据库
        Ds ds = new Ds();
        BeanUtils.copyProperties(dsDto, ds);
        ds.setCreateTime(new Date());
        dsMapper.insertSelective(ds);

        //2、动态添加新的数据源
        try {
            String password = AESUtils.decrypt(dsDto.getPassword(), secretKey);
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl(dsDto.getUrl());
            dataSource.setUsername(dsDto.getUsername());
            dataSource.setPassword(password);
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            dataSourceMap.put(dsDto.getName(), dataSource);
            dynamicDataSourceManager.setTargetDataSources(dataSourceMap);
            dynamicDataSourceManager.afterPropertiesSet();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return true;
    }

要完成第一个功能简单,调用mapper中的方法来向数据库中插入数据

mpper接口中方法:

int insertSelective(Ds record);

sql映射文件




  
    
    
    
    
    
    
  
  
    id, `name`, url, username, `password`, create_time
  



    insert into ds
    
      
        `name`,
      
      
        url,
      
      
        username,
      
      
        `password`,
      
      
        create_time,
      
    
    
      
        #{name,jdbcType=VARCHAR},
      
      
        #{url,jdbcType=VARCHAR},
      
      
        #{username,jdbcType=VARCHAR},
      
      
        #{password,jdbcType=VARCHAR},
      
      
        #{createTime,jdbcType=TIMESTAMP},
      
    
  
  

接下来重点介绍如何动态创建数据源、切换数据源

五、数据源动态添加及切换

第一步:新增数据源理类,用来动态创建切换数据源

这里使用了ThreadLoacal

/**
 * 动态数据源管理类,用于管理和切换数据源
 */
public class DynamicDataSourceManager extends AbstractRoutingDataSource {

    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    public DynamicDataSourceManager(DruidDataSource druidDataSource,
                                    Map targetDataSources) {
        super.setDefaultTargetDataSource(druidDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return contextHolder.get();
    }

    public static void setDataSource(String dataSourceKey) {
        contextHolder.set(dataSourceKey);
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }
}

第二步:配置默认数据源

/**
 * 配置类用来管理动态数据源
 */
@Configuration
public class DataSourceConfig {

    @Value("${spring.datasource.druid.url}")
    private String url;

    @Value("${spring.datasource.druid.username}")
    private String username;

    @Value("${spring.datasource.druid.password}")
    private String password;

    @Value("${spring.datasource.druid.driver-class-name}")
    private String driverClassName;

    // 注意:这里要使用Autowired,不要使用Bean,否则启动后mybatis的mapper不会加载
    @Autowired
    public DruidDataSource defaultDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driverClassName);
        // System.out.println(dataSource.getUrl());
        return dataSource;
    }

    @Bean
    public DynamicDataSourceManager dynamicDataSourceManager() {
        Map targetDataSources = new HashMap<>();
        targetDataSources.put("defaultDs", defaultDataSource());
        return new DynamicDataSourceManager(defaultDataSource(), targetDataSources);

    }
}

从上面的代码可以看出来我们默认的数据源是"defaultDs"

在项目启动的时候就会去构造它这个默认的数据源。

注意:我们实际创建的的方法上使用的注解是@Autowired,不能使用@Bean,是因为我们要让它存在依赖关系而不是直接创建一个Bean

第三步:我们在项目启动的时候就加载数据中所有的数据连接信息创建好数据源

@Component
@RequiredArgsConstructor
@Slf4j
public class DataSourceInitializer implements CommandLineRunner {


    private final DynamicDataSourceManager dynamicDataSourceManager;

    private final DsService dsService;

    @Value("${crypto.secret}")
    private String secretKey;

    @Override
    public void run(String... args) throws Exception {

        log.info("数据源初始化开始...");

        // 获取所有数据源
        List dsList = dsService.selectAll();

        // 创建数据源map
        Map dataSourceMap = new HashMap<>();

        for (Ds ds : dsList) {
            String password = AESUtils.decrypt(ds.getPassword(), secretKey);
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl(ds.getUrl());
            dataSource.setUsername(ds.getUsername());
            dataSource.setPassword(password);
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");

            dataSourceMap.put(ds.getName(), dataSource);
        }

        // 设置目标数据源
        dynamicDataSourceManager.setTargetDataSources(dataSourceMap);
        dynamicDataSourceManager.afterPropertiesSet();
        log.info("数据源初始化完成...");
    }
}

六、测试

新增一个controller来测试动态数据源的测试

@RestController
@RequiredArgsConstructor
public class TestController {

    private final TestMapper testMapper;
    private final DsService dsService;


    @GetMapping("/test")
    public List getAllTest(@RequestParam("ds") String datasourceName){
        // System.out.println("datasourceName:"+datasourceName);
        // DynamicDataSourceManager.setDataSource("test");
        dsService.switchDataSource(datasourceName);
        List tests = testMapper.selectAll();
        dsService.clearDataSource();
        return tests;
    }
}

其中service接口及实现类

void switchDataSource(String dataSourceName);

void clearDataSource();

List selectAll();
    @Override
    public void switchDataSource(String dataSourceName) {
        DynamicDataSourceManager.setDataSource(dataSourceName);
    }

    @Override
    public void clearDataSource() {
        DynamicDataSourceManager.clearDataSource();
    }

    @Override
    public List selectAll() {
        return dsMapper.selectAll();
    }

动态数据源管理类中实现如下:

    public static void setDataSource(String dataSourceKey) {
        contextHolder.set(dataSourceKey);
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }

mapper及sql映射比较简单就是查询一下test表中的记录

@Mapper
public interface TestMapper {

    List selectAll();
}




    

接下来我们看看我们ds表示的记录:

启动项目:

1、访问项目时使用test数据源

2、访问项目使用ds1数据源

3、使用默认数据源defaultDs

相关推荐

告别手动操作:一键多工作表合并的实用方法

通常情况下,我们需要将同一工作簿内不同工作表中的数据进行合并处理。如何快速有效地完成这些数据的整合呢?这主要取决于需要合并的源数据的结构。...

【MySQL技术专题】「优化技术系列」常用SQL的优化方案和技术思路

概述前面我们介绍了MySQL中怎么样通过索引来优化查询。日常开发中,除了使用查询外,我们还会使用一些其他的常用SQL,比如INSERT、GROUPBY等。对于这些SQL语句,我们该怎么样进行优化呢...

9.7寸视网膜屏原道M9i双系统安装教程

泡泡网平板电脑频道4月17日原道M9i采用Win8安卓双系统,对于喜欢折腾的朋友来说,刷机成了一件难事,那么原道M9i如何刷机呢?下面通过详细地图文,介绍原道M9i的刷机操作过程,在刷机的过程中,要...

如何做好分布式任务调度——Scheduler 的一些探索

作者:张宇轩,章逸,曾丹初识Scheduler找准定位:分布式任务调度平台...

mysqldump备份操作大全及相关参数详解

mysqldump简介mysqldump是用于转储MySQL数据库的实用程序,通常我们用来迁移和备份数据库;它自带的功能参数非常多,文中列举出几乎所有常用的导出操作方法,在文章末尾将所有的参数详细说明...

大厂面试冲刺,Java“实战”问题三连,你碰到了哪个?

推荐学习...

亿级分库分表,如何丝滑扩容、如何双写灰度

以下是基于亿级分库分表丝滑扩容与双写灰度设计方案,结合架构图与核心流程说明:一、总体设计目标...

MYSQL表设计规范(mysql表设计原则)

日常工作总结,不是通用规范一、表设计库名、表名、字段名必须使用小写字母,“_”分割。...

怎么解决MySQL中的Duplicate entry错误?

在使用MySQL数据库时,我们经常会遇到Duplicateentry错误,这是由于插入或更新数据时出现了重复的唯一键值。这种错误可能会导致数据的不一致性和完整性问题。为了解决这个问题,我们可以采取以...

高并发下如何防重?(高并发如何防止重复)

前言最近测试给我提了一个bug,说我之前提供的一个批量复制商品的接口,产生了重复的商品数据。...

性能压测数据告诉你MySQL和MariaDB该怎么选

1.压测环境为了尽可能的客观公正,本次选择同一物理机上的两台虚拟机,一台用作数据库服务器,一台用作运行压测工具mysqlslap,操作系统均为UbuntuServer22.04LTS。...

屠龙之技 --sql注入 不值得浪费超过十天 实战中sqlmap--lv 3通杀全国

MySQL小结发表于2020-09-21分类于知识整理阅读次数:本文字数:67k阅读时长≈1:01...

破防了,谁懂啊家人们:记一次 mysql 问题排查

作者:温粥一、前言谁懂啊家人们,作为一名java开发,原来以为mysql这东西,写写CRUD,不是有手就行吗;你说DDL啊,不就是设计个表结构,搞几个索引吗。...

SpringBoot系列Mybatis之批量插入的几种姿势

...

MySQL 之 Performance Schema(mysql安装及配置超详细教程)

MySQL之PerformanceSchema介绍PerformanceSchema提供了在数据库运行时实时检查MySQL服务器的内部执行情况的方法,通过监视MySQL服务器的事件来实现监视内...

取消回复欢迎 发表评论: