Spring Boot + Mybatis 实现动态数据源案例分析

动态数据源

在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库。又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据源方案进行解决。接下来,我们就来讲解如何实现动态数据源,以及在过程中剖析动态数据源背后的实现原理。

实现案例

本教程案例基于 Spring Boot + Mybatis + MySQL 实现。

数据库设计

首先需要安装好MySQL数据库,新建数据库 master,slave,分别创建用户表,用来测试数据源,SQL脚本如下。

-- ----------------------------------------------------
-- 用户
-- ----------------------------------------------------
-- Table structure for `sys_user`
-- ----------------------------------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
 `name` varchar(50) NOT NULL COMMENT '用户名',
 `password` varchar(100) COMMENT '密码',
 `salt` varchar(40) COMMENT '盐',
 `email` varchar(100) COMMENT '邮箱',
 `mobile` varchar(100) COMMENT '手机号',
 `status` tinyint COMMENT '状态 0:禁用 1:正常',
 `dept_id` bigint(20) COMMENT '机构ID',
 `create_by` varchar(50) COMMENT '创建人',
 `create_time` datetime COMMENT '创建时间',
 `last_update_by` varchar(50) COMMENT '更新人',
 `last_update_time` datetime COMMENT '更新时间',
 `del_flag` tinyint DEFAULT 0 COMMENT '是否删除 -1:已删除 0:正常',
 PRIMARY KEY (`id`),
 UNIQUE INDEX (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';

新建工程

新建一个Spring Boot工程,最终代码结构如下。

添加依赖

添加Spring Boot,Spring Aop,Mybatis,MySQL,Swagger相关依赖。Swagger方便用来测试接口。

pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>top.ivan.demo</groupId>
 <artifactId>springboot-dynamic-datasource</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>springboot-dynamic-datasource</name>
 <description>Demo project for Spring Boot</description>

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.4.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
  <mybatis.spring.version>1.3.2</mybatis.spring.version>
  <swagger.version>2.8.0</swagger.version>
 </properties>

 <dependencies>
   <!-- spring boot -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
  <!-- spring aop -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>
  <!-- mybatis -->
  <dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <version>${mybatis.spring.version}</version>
  </dependency>
  <!-- mysql -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <!-- swagger -->
  <dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>${swagger.version}</version>
  </dependency>
  <dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>${swagger.version}</version>
  </dependency>
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
  </plugins>
 </build>

</project>

配置文件

修改配置文件,添加两个数据源,可以是同一个主机地址的两个数据库master,slave,也可是两个不同主机的地址,根据实际情况配置。

application.yml

spring:
 datasource:
 master:
  driver-class-name: com.mysql.jdbc.Driver
  type: com.zaxxer.hikari.HikariDataSource
  jdbcUrl: jdbc:mysql://127.0.0.1:3306/master?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
  username: root
  password: 123
 slave:
  driver-class-name: com.mysql.jdbc.Driver
  type: com.zaxxer.hikari.HikariDataSource
  jdbcUrl: jdbc:mysql://127.0.0.1:3306/slave?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
  username: root
  password: 123

启动类

启动类添加 exclude = {DataSourceAutoConfiguration.class}, 以禁用数据源默认自动配置。

数据源默认自动配置会读取 spring.datasource.* 的属性创建数据源,所以要禁用以进行定制。

@ComponentScan(basePackages = "com.louis.springboot") 是扫描范围,都知道不用多说。

DynamicDatasourceApplication.java

package com.louis.springboot.dynamic.datasource;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

/**
 * 启动器
 * @author Louis
 * @date Oct 31, 2018
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) // 禁用数据源自动配置
@ComponentScan(basePackages = "com.louis.springboot")
public class DynamicDatasourceApplication {

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

数据源配置类

创建一个数据源配置类,主要做以下几件事情:

1. 配置 dao,model,xml mapper文件的扫描路径。

2. 注入数据源配置属性,创建master、slave数据源。

3. 创建一个动态数据源,并装入master、slave数据源。

4. 将动态数据源设置到SQL会话工厂和事务管理器。

如此,当进行数据库操作时,就会通过我们创建的动态数据源去获取要操作的数据源了。

package com.louis.springboot.dynamic.datasource.config;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
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 org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.louis.springboot.dynamic.datasource.dds.DynamicDataSource;

/**
 * Mybatis配置
 * @author Louis
 * @date Oct 31, 2018
 */
@Configuration
@MapperScan(basePackages = {"com.louis.**.dao"}) // 扫描DAO
public class MybatisConfig {

 @Bean("master")
 @Primary
 @ConfigurationProperties(prefix = "spring.datasource.master")
 public DataSource master() {
  return DataSourceBuilder.create().build();
 }

 @Bean("slave")
 @ConfigurationProperties(prefix = "spring.datasource.slave")
 public DataSource slave() {
  return DataSourceBuilder.create().build();
 }

 @Bean("dynamicDataSource")
 public DataSource dynamicDataSource() {
  DynamicDataSource dynamicDataSource = new DynamicDataSource();
  Map<Object, Object> dataSourceMap = new HashMap<>(2);
  dataSourceMap.put("master", master());
  dataSourceMap.put("slave", slave());
  // 将 master 数据源作为默认指定的数据源
  dynamicDataSource.setDefaultDataSource(master());
  // 将 master 和 slave 数据源作为指定的数据源
  dynamicDataSource.setDataSources(dataSourceMap);
  return dynamicDataSource;
 }

 @Bean
 public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
  SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
  // 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource作为数据源则不能实现切换
  sessionFactory.setDataSource(dynamicDataSource());
  sessionFactory.setTypeAliasesPackage("com.louis.**.model"); // 扫描Model
  PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  sessionFactory.setMapperLocations(resolver.getResources("classpath*:**/sqlmap/*.xml")); // 扫描映射文件
  return sessionFactory;
 }

 @Bean
 public PlatformTransactionManager transactionManager() {
  // 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
  return new DataSourceTransactionManager(dynamicDataSource());
 }
}

动态数据源类

我们上一步把这个动态数据源设置到了SQL会话工厂和事务管理器,这样在操作数据库时就会通过动态数据源类来获取要操作的数据源了。

动态数据源类集成了Spring提供的AbstractRoutingDataSource类,AbstractRoutingDataSource 中 获取数据源的方法就是 determineTargetDataSource,而此方法又通过 determineCurrentLookupKey 方法获取查询数据源的key。

所以如果我们需要动态切换数据源,就可以通过以下两种方式定制:

1. 覆写 determineCurrentLookupKey 方法

通过覆写 determineCurrentLookupKey 方法,从一个自定义的 DynamicDataSourceContextHolder.getDataSourceKey() 获取数据源key值,这样在我们想动态切换数据源的时候,只要通过  DynamicDataSourceContextHolder.setDataSourceKey(key)  的方式就可以动态改变数据源了。这种方式要求在获取数据源之前,要先初始化各个数据源到 DynamicDataSource 中,我们案例就是采用这种方式实现的,所以在 MybatisConfig 中把master和slave数据源都事先初始化到DynamicDataSource 中。

2. 可以通过覆写 determineTargetDataSource,因为数据源就是在这个方法创建并返回的,所以这种方式就比较自由了,支持到任何你希望的地方读取数据源信息,只要最终返回一个 DataSource 的实现类即可。比如你可以到数据库、本地文件、网络接口等方式读取到数据源信息然后返回相应的数据源对象就可以了。

DynamicDataSource.java

package com.louis.springboot.dynamic.datasource.dds;

import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源实现类
 * @author Louis
 * @date Oct 31, 2018
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

  /**
   * 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源
   * 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
   */
  @Override
  protected DataSource determineTargetDataSource() {
    return super.determineTargetDataSource();
  }

  /**
   * 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
   */
  @Override
  protected Object determineCurrentLookupKey() {
    return DynamicDataSourceContextHolder.getDataSourceKey();
  }

  /**
   * 设置默认数据源
   * @param defaultDataSource
   */
  public void setDefaultDataSource(Object defaultDataSource) {
    super.setDefaultTargetDataSource(defaultDataSource);
  }

  /**
   * 设置数据源
   * @param dataSources
   */
  public void setDataSources(Map<Object, Object> dataSources) {
    super.setTargetDataSources(dataSources);
    // 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
    DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
  }
}

数据源上下文

动态数据源的切换主要是通过调用这个类的方法来完成的。在任何想要进行切换数据源的时候都可以通过调用这个类的方法实现切换。比如系统登录时,根据用户信息调用这个类的数据源切换方法切换到用户对应的数据库。

主要方法介绍:

1. 切换数据源

在任何想要进行切换数据源的时候都可以通过调用这个类的方法实现切换。

  /**
   * 切换数据源
   * @param key
   */
  public static void setDataSourceKey(String key) {
    contextHolder.set(key);
  }

2. 重置数据源

将数据源重置回默认的数据源。默认数据源通过 DynamicDataSource.setDefaultDataSource(ds) 进行设置。

  /**
   * 重置数据源
   */
  public static void clearDataSourceKey() {
    contextHolder.remove();
  }

3. 获取当前数据源key

  /**
   * 获取数据源
   * @return
   */
  public static String getDataSourceKey() {
    return contextHolder.get();
  }

完整代码如下

DynamicDataSourceContextHolder.java

package com.louis.springboot.dynamic.datasource.dds;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 动态数据源上下文
 * @author Louis
 * @date Oct 31, 2018
 */
public class DynamicDataSourceContextHolder {

  private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
    /**
     * 将 master 数据源的 key作为默认数据源的 key
     */
    @Override
    protected String initialValue() {
      return "master";
    }
  };

  /**
   * 数据源的 key集合,用于切换时判断数据源是否存在
   */
  public static List<Object> dataSourceKeys = new ArrayList<>();

  /**
   * 切换数据源
   * @param key
   */
  public static void setDataSourceKey(String key) {
    contextHolder.set(key);
  }

  /**
   * 获取数据源
   * @return
   */
  public static String getDataSourceKey() {
    return contextHolder.get();
  }

  /**
   * 重置数据源
   */
  public static void clearDataSourceKey() {
    contextHolder.remove();
  }

  /**
   * 判断是否包含数据源
   * @param key 数据源key
   * @return
   */
  public static boolean containDataSourceKey(String key) {
    return dataSourceKeys.contains(key);
  }

  /**
   * 添加数据源keys
   * @param keys
   * @return
   */
  public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
    return dataSourceKeys.addAll(keys);
  }
}

注解式数据源

到这里,在任何想要动态切换数据源的时候,只要调用  DynamicDataSourceContextHolder.setDataSourceKey(key)  就可以完成了。

接下来我们实现通过注解的方式来进行数据源的切换,原理就是添加注解(如@DataSource(value="master")),然后实现注解切面进行数据源切换。

创建一个动态数据源注解,拥有一个value值,用于标识要切换的数据源的key。

DataSource.java

package com.louis.springboot.dynamic.datasource.dds;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 动态数据源注解
 * @author Louis
 * @date Oct 31, 2018
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

  /**
   * 数据源key值
   * @return
   */
  String value();

}

创建一个AOP切面,拦截带 @DataSource 注解的方法,在方法执行前切换至目标数据源,执行完成后恢复到默认数据源。

DynamicDataSourceAspect.java

package com.louis.springboot.dynamic.datasource.dds;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 动态数据源切换处理器
 * @author Louis
 * @date Oct 31, 2018
 */
@Aspect
@Order(-1) // 该切面应当先于 @Transactional 执行
@Component
public class DynamicDataSourceAspect {

  /**
   * 切换数据源
   * @param point
   * @param dataSource
   */
  @Before("@annotation(dataSource))")
  public void switchDataSource(JoinPoint point, DataSource dataSource) {
    if (!DynamicDataSourceContextHolder.containDataSourceKey(dataSource.value())) {
      System.out.println("DataSource [{}] doesn't exist, use default DataSource [{}] " + dataSource.value());
    } else {
      // 切换数据源
      DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value());
      System.out.println("Switch DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
        + "] in Method [" + point.getSignature() + "]");
    }
  }

  /**
   * 重置数据源
   * @param point
   * @param dataSource
   */
  @After("@annotation(dataSource))")
  public void restoreDataSource(JoinPoint point, DataSource dataSource) {
    // 将数据源置为默认数据源
    DynamicDataSourceContextHolder.clearDataSourceKey();
    System.out.println("Restore DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
      + "] in Method [" + point.getSignature() + "]");
  }
}

到这里,动态数据源相关的处理代码就完成了。

编写用户业务代码

接下来编写用户查询业务代码,用来进行测试,只需添加一个查询接口即可。

编写一个控制器,包含两个查询方法,分别注解 master 和 slave 数据源。

SysUserController.java
package com.louis.springboot.dynamic.datasource.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.louis.springboot.dynamic.datasource.dds.DataSource;
import com.louis.springboot.dynamic.datasource.service.SysUserService;
/**
 * 用户控制器
 * @author Louis
 * @date Oct 31, 2018
 */
@RestController
@RequestMapping("user")
public class SysUserController {
  @Autowired
  private SysUserService sysUserService;
  @DataSource(value="master")
  @PostMapping(value="/findAll")
  public Object findAll() {
    return sysUserService.findAll();
  }
  @DataSource(value="slave")
  @PostMapping(value="/findAll2")
  public Object findAll2() {
    return sysUserService.findAll();
  }
}

下面是正常的业务代码,没有什么好说明的,直接贴代码了。

SysUser.java
public class SysUser {
  private Long id;
  private String name;
  private String password;
  private String salt;
  private String email;
  private String mobile;
  private Byte status;
  private Long deptId;
  private String deptName;
  private Byte delFlag;
  private String createBy;
  private Date createTime;
  private String lastUpdateBy;
  private Date lastUpdateTime;
  ...setter and getter
}
SysUserMapper.java
package com.louis.springboot.dynamic.datasource.dao;
import java.util.List;
import com.louis.springboot.dynamic.datasource.model.SysUser;
public interface SysUserMapper {
  int deleteByPrimaryKey(Long id);
  int insert(SysUser record);
  int insertSelective(SysUser record);
  SysUser selectByPrimaryKey(Long id);
  int updateByPrimaryKeySelective(SysUser record);
  int updateByPrimaryKey(SysUser record);
  List<SysUser> findAll();
}

SysUserMapper.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.louis.springboot.dynamic.datasource.dao.SysUserMapper">
 <resultMap id="BaseResultMap" type="com.louis.springboot.dynamic.datasource.model.SysUser">
  <id column="id" jdbcType="BIGINT" property="id" />
  <result column="name" jdbcType="VARCHAR" property="name" />
  <result column="password" jdbcType="VARCHAR" property="password" />
  <result column="salt" jdbcType="VARCHAR" property="salt" />
  <result column="email" jdbcType="VARCHAR" property="email" />
  <result column="mobile" jdbcType="VARCHAR" property="mobile" />
  <result column="status" jdbcType="TINYINT" property="status" />
  <result column="dept_id" jdbcType="BIGINT" property="deptId" />
  <result column="create_by" jdbcType="BIGINT" property="createBy" />
  <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
  <result column="last_update_by" jdbcType="BIGINT" property="lastUpdateBy" />
  <result column="last_update_time" jdbcType="TIMESTAMP" property="lastUpdateTime" />
  <result column="del_flag" jdbcType="TINYINT" property="delFlag" />
 </resultMap>
 <sql id="Base_Column_List">
  id, name, password, salt, email, mobile, status, dept_id, create_by, create_time,
  last_update_by, last_update_time, del_flag
 </sql>
 <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
  select
  <include refid="Base_Column_List" />
  from sys_user
  where id = #{id,jdbcType=BIGINT}
 </select>
 <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
  delete from sys_user
  where id = #{id,jdbcType=BIGINT}
 </delete>
 <insert id="insert" parameterType="com.louis.springboot.dynamic.datasource.model.SysUser">
  insert into sys_user (id, name, password,
   salt, email, mobile,
   status, dept_id, create_by,
   create_time, last_update_by, last_update_time,
   del_flag)
  values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR},
   #{salt,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{mobile,jdbcType=VARCHAR},
   #{status,jdbcType=TINYINT}, #{deptId,jdbcType=BIGINT}, #{createBy,jdbcType=BIGINT},
   #{createTime,jdbcType=TIMESTAMP}, #{lastUpdateBy,jdbcType=BIGINT}, #{lastUpdateTime,jdbcType=TIMESTAMP},
   #{delFlag,jdbcType=TINYINT})
 </insert>
 <insert id="insertSelective" parameterType="com.louis.springboot.dynamic.datasource.model.SysUser">
  insert into sys_user
  <trim prefix="(" suffix=")" suffixOverrides=",">
   <if test="id != null">
    id,
   </if>
   <if test="name != null">
    name,
   </if>
   <if test="password != null">
    password,
   </if>
   <if test="salt != null">
    salt,
   </if>
   <if test="email != null">
    email,
   </if>
   <if test="mobile != null">
    mobile,
   </if>
   <if test="status != null">
    status,
   </if>
   <if test="deptId != null">
    dept_id,
   </if>
   <if test="createBy != null">
    create_by,
   </if>
   <if test="createTime != null">
    create_time,
   </if>
   <if test="lastUpdateBy != null">
    last_update_by,
   </if>
   <if test="lastUpdateTime != null">
    last_update_time,
   </if>
   <if test="delFlag != null">
    del_flag,
   </if>
  </trim>
  <trim prefix="values (" suffix=")" suffixOverrides=",">
   <if test="id != null">
    #{id,jdbcType=BIGINT},
   </if>
   <if test="name != null">
    #{name,jdbcType=VARCHAR},
   </if>
   <if test="password != null">
    #{password,jdbcType=VARCHAR},
   </if>
   <if test="salt != null">
    #{salt,jdbcType=VARCHAR},
   </if>
   <if test="email != null">
    #{email,jdbcType=VARCHAR},
   </if>
   <if test="mobile != null">
    #{mobile,jdbcType=VARCHAR},
   </if>
   <if test="status != null">
    #{status,jdbcType=TINYINT},
   </if>
   <if test="deptId != null">
    #{deptId,jdbcType=BIGINT},
   </if>
   <if test="createBy != null">
    #{createBy,jdbcType=BIGINT},
   </if>
   <if test="createTime != null">
    #{createTime,jdbcType=TIMESTAMP},
   </if>
   <if test="lastUpdateBy != null">
    #{lastUpdateBy,jdbcType=BIGINT},
   </if>
   <if test="lastUpdateTime != null">
    #{lastUpdateTime,jdbcType=TIMESTAMP},
   </if>
   <if test="delFlag != null">
    #{delFlag,jdbcType=TINYINT},
   </if>
  </trim>
 </insert>
 <update id="updateByPrimaryKeySelective" parameterType="com.louis.springboot.dynamic.datasource.model.SysUser">
  update sys_user
  <set>
   <if test="name != null">
    name = #{name,jdbcType=VARCHAR},
   </if>
   <if test="password != null">
    password = #{password,jdbcType=VARCHAR},
   </if>
   <if test="salt != null">
    salt = #{salt,jdbcType=VARCHAR},
   </if>
   <if test="email != null">
    email = #{email,jdbcType=VARCHAR},
   </if>
   <if test="mobile != null">
    mobile = #{mobile,jdbcType=VARCHAR},
   </if>
   <if test="status != null">
    status = #{status,jdbcType=TINYINT},
   </if>
   <if test="deptId != null">
    dept_id = #{deptId,jdbcType=BIGINT},
   </if>
   <if test="createBy != null">
    create_by = #{createBy,jdbcType=BIGINT},
   </if>
   <if test="createTime != null">
    create_time = #{createTime,jdbcType=TIMESTAMP},
   </if>
   <if test="lastUpdateBy != null">
    last_update_by = #{lastUpdateBy,jdbcType=BIGINT},
   </if>
   <if test="lastUpdateTime != null">
    last_update_time = #{lastUpdateTime,jdbcType=TIMESTAMP},
   </if>
   <if test="delFlag != null">
    del_flag = #{delFlag,jdbcType=TINYINT},
   </if>
  </set>
  where id = #{id,jdbcType=BIGINT}
 </update>
 <update id="updateByPrimaryKey" parameterType="com.louis.springboot.dynamic.datasource.model.SysUser">
  update sys_user
  set name = #{name,jdbcType=VARCHAR},
   password = #{password,jdbcType=VARCHAR},
   salt = #{salt,jdbcType=VARCHAR},
   email = #{email,jdbcType=VARCHAR},
   mobile = #{mobile,jdbcType=VARCHAR},
   status = #{status,jdbcType=TINYINT},
   dept_id = #{deptId,jdbcType=BIGINT},
   create_by = #{createBy,jdbcType=BIGINT},
   create_time = #{createTime,jdbcType=TIMESTAMP},
   last_update_by = #{lastUpdateBy,jdbcType=BIGINT},
   last_update_time = #{lastUpdateTime,jdbcType=TIMESTAMP},
   del_flag = #{delFlag,jdbcType=TINYINT}
  where id = #{id,jdbcType=BIGINT}
 </update>
 <select id="findAll" resultMap="BaseResultMap">
  select
  <include refid="Base_Column_List" />
  from sys_user
 </select>
</mapper>

SysUserService.java

package com.louis.springboot.dynamic.datasource.service;
import java.util.List;
import com.louis.springboot.dynamic.datasource.model.SysUser;
/**
 * 用户管理
 * @author Louis
 * @date Oct 31, 2018
 */
public interface SysUserService {
  /**
   * 查找全部用户信息
   * @return
   */
  List<SysUser> findAll();
}
SysUserServiceImpl.java
package com.louis.springboot.dynamic.datasource.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.louis.springboot.dynamic.datasource.dao.SysUserMapper;
import com.louis.springboot.dynamic.datasource.model.SysUser;
import com.louis.springboot.dynamic.datasource.service.SysUserService;
@Service
public class SysUserServiceImpl implements SysUserService {
  @Autowired
  private SysUserMapper sysUserMapper;
  /**
   * 查找全部用户信息
   * @return
   */
  public List<SysUser> findAll() {
    return sysUserMapper.findAll();
  }
}

测试效果

启动系统,访问  http://localhost:8080/swagger-ui.html ,分别测试两个接口,成功返回数据。

user/findAll (master数据源)

user/findAll2 (slave数据源)

源码下载

码云:https://gitee.com/liuge1988/spring-boot-demo.git

总结

以上所述是小编给大家介绍的Spring Boot + Mybatis 实现动态数据源案例分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Spring Boot+Mybatis+Druid+PageHelper实现多数据源并分页的方法

    前言 本篇文章主要讲述的是SpringBoot整合Mybatis.Druid和PageHelper 并实现多数据源和分页.其中SpringBoot整合Mybatis这块,在之前的的一篇文章中已经讲述了,这里就不过多说明了.重点是讲述在多数据源下的如何配置使用Druid和PageHelper . Druid介绍和使用 在使用Druid之前,先来简单的了解下Druid. Druid是一个数据库连接池.Druid可以说是目前最好的数据库连接池!因其优秀的功能.性能和扩展性方面,深受开发人员的青睐. D

  • 详解springboot采用多数据源对JdbcTemplate配置的方法

    springboot多数据源配置,代码如下 DataSourceConfig package com.rookie.bigdata.config; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.

  • spring实现动态切换、添加数据源及源码分析

    前言 对于数据量在1千万,单个mysql数据库就可以支持,但是如果数据量大于这个数的时候,例如1亿,那么查询的性能就会很低.此时需要对数据库做水平切分,常见的做法是按照用户的账号进行hash,然后选择对应的数据库. 最近公司项目需求,由于要兼容老系统的数据库结构,需要搭建一个 可以动态切换.添加数据源的后端服务. 参考了过去的项目,通过配置多个SqlSessionFactory 来实现多数据源,这么做的话,未免过于笨重,而且无法实现动态添加数据源这个需求 通过 spring AbstractRo

  • Spring配置多数据源切换

    多数据源切换 db.properties #MySQL jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test?autoReconnect=true&characterEncoding=utf-8 jdbc.username=root jdbc.password=admin #定义初始连接数 initialSize=0 #定义最大连接数 maxActive=1000 #定义最大空闲 maxIdle=2

  • springboot v2.0.3版本多数据源配置方法

    本篇分享的是springboot多数据源配置,在从springboot v1.5版本升级到v2.0.3时,发现之前写的多数据源的方式不可用了,捕获错误信息如: 异常:jdbcUrl is required with driverClassName. 先来说下之前的多数据源配置如: spring: datasource: url: jdbc:sqlserver://192.168.122.111;DatabaseName=flight username: sa password: 1234.abc

  • spring boot + mybatis实现动态切换数据源实例代码

    前言 前几天有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源.我上网收集了一些资料整合了一下,虽然最后这个需求不了了之了,但是多数据源动态切换还是蛮好用的,所以记录一下,或许以后有用呢?或者自己感兴趣又想玩呢! 下面话不多说了,随着小编来一起看看详细的介绍吧 方法如下: 1.加个依赖 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybat

  • 通过Spring Boot配置动态数据源访问多个数据库的实现代码

    之前写过一篇博客<Spring+Mybatis+Mysql搭建分布式数据库访问框架>描述如何通过Spring+Mybatis配置动态数据源访问多个数据库.但是之前的方案有一些限制(原博客中也描述了):只适用于数据库数量不多且固定的情况.针对数据库动态增加的情况无能为力. 下面讲的方案能支持数据库动态增删,数量不限. 数据库环境准备 下面一Mysql为例,先在本地建3个数据库用于测试.需要说明的是本方案不限数据库数量,支持不同的数据库部署在不同的服务器上.如图所示db_project_001.d

  • Servlet+MyBatis项目转Spring Cloud微服务,多数据源配置修改建议

    一.项目需求 在开发过程中,由于技术的不断迭代,为了提高开发效率,需要对原有项目的架构做出相应的调整. 二.存在的问题 为了不影响项目进度,架构调整初期只是把项目做了简单的maven管理,引入springboot并未做spring cloud微服务处理.但随着项目的进一步开发,急需拆分现有业务,做微服务处理.因此架构上的短板日益突出.spring cloud config 无法完全应用,每次项目部署需要修改大量配置文件.严重影响开发效率,因此便萌生了对项目架构再次调整的决心. 三.调整建议 为了

  • spring+Jpa多数据源配置的方法示例

    今天临下班时遇到了一个需求,我的管理平台需要从不同的数据库中获取数据信息,这就需要进行Spring的多数据源配置,对于这种配置,第一次永远都是痛苦的,不过经历了这次的折磨,今后肯定会对这种配置印象深刻.我们这里简单回顾一下流程. 我们配置了两个数据库,一个是公司的数据库,另一个是我本地的一个数据库.首先是application.yml的配置(其中对于公司的数据库我们采取了假的地址,而本机的数据库是真是存在对应的表和库的) 数据库信息: 数据表信息: 1.application.yml datas

  • Spring Boot 2.0多数据源配置方法实例详解

    两个数据库实例,一个负责读,一个负责写. datasource-reader: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://192.168.43.61:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false username: icbc password: icbc driver-class-na

随机推荐