如果不熟悉MyBatis-Plus
可以先看下这篇文章:SpringBoot2.x整合MyBatis-Plus
本文主要讲解使用注解的方式配置多数据源
SpringBoot+AOP构建多数据源的切换实践
https://github.com/qidasheng2012/springbooot2.x-mybatis-plus-datasources
本文只会把核心的一部分代码粘贴出来进行讲解,道友可以根据上面的GitHub地址clone下项目,修改为自己的数据库配置,项目即可跑的起来
下图是项目提供的建表sql,建system和server两个数据库,分别执行下面的sql
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.0.RELEASEversion>
<relativePath/>
parent>
<groupId>com.seawaterbtgroupId>
<artifactId>data-sourcesartifactId>
<version>1.0.0version>
<packaging>jarpackaging>
<name>data-sourcesname>
<description>SpringBoot2.x + MyBatis-plus + swagger2 + 多数据源description>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<mybatisPlus.version>3.2.0mybatisPlus.version>
<druid.version>1.1.21druid.version>
<swagger.version>2.9.2swagger.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>${mybatisPlus.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>${druid.version}version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>${swagger.version}version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>${swagger.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
实际开发过程中会有多个环境,所以项目给了dev、uat、pro三个环境的配置文件,这里只展示dev环境的,请根据自己的数据库进行修改
server:
servlet:
context-path: /ssm
spring:
profiles:
active: dev
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 设置访问druid监控页的账号和密码,默认没有
stat-view-servlet:
login-username: admin
login-password: admin
# 配置扩展插件,配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
use-global-data-source-stat: true
# 连接池配置
#连接池建立时创建的初始化连接数
initial-size: 5
#连接池中最大的活跃连接数
max-active: 20
#连接池中最小的活跃连接数
min-idle: 5
#获取连接时最大等待时间,单位毫秒
max-wait: 60000
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-open-prepared-statements: 20
#在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位,一般比minEvictableIdleTimeMillis小
time-between-eviction-runs-millis: 60000
#连接池中连接,在时间段内一直空闲,被逐出连接池的时间(1000*60*60),以毫秒为单位
min-evictable-idle-time-millis: 300000
# 连接有效性检测
#是否在连接空闲一段时间后检测其可用性
test-while-idle: true
#是否在获得连接后检测其可用性
test-on-borrow: false
#是否在连接放回连接池后检测其可用性
test-on-return: false
#mysql中为 select 'x'
#oracle中为 select 1 from dual
validation-query: SELECT 'x'
mybatis-plus:
# 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
# 如果是放在resource目录 classpath:/mapper/*Mapper.xml
mapper-locations: classpath:/mapper/*Mapper.xml
# 别名包扫描,多个package用逗号或者分号分隔
type-aliases-package: com.ssm.entity
configuration:
#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
map-underscore-to-camel-case: true
cache-enabled: false
#配置JdbcTypeForNull, oracle数据库必须配置
jdbc-type-for-null: 'null'
global-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
db-column-underline: true
#mp2.3+ 全局表前缀 t_
table-prefix: t_
#刷新mapper 调试神器
refresh-mapper: true
#数据库大写下划线转换
capital-mode: true
#逻辑删除配置(下面3个配置)
logic-delete-value: 4
logic-not-delete-value: 0
定义了两个数据源配置
server:
port: 81
spring:
datasource:
druid:
db1:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/system?serverTimezone=Asia/Shanghai&characterEncoding=utf8
username: root
password: 123456
db2:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/server?serverTimezone=Asia/Shanghai&characterEncoding=utf8
username: root
password: 123456
定义数据源枚举
package com.ssm.enums;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum DataSourceEnum {
DB1("db1"),
DB2("db2");
private String value;
}
定义数据源注解
package com.ssm.annotation;
import com.ssm.enums.DataSourceEnum;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
DataSourceEnum value() default DataSourceEnum.DB1;
}
定义数据源切面
package com.ssm.aop;
import com.ssm.annotation.DataSource;
import com.ssm.multiple.MultipleDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Slf4j
@Aspect
@Order(-1)
public class DataSourceAspect {
@Pointcut("@within(com.ssm.annotation.DataSource) || @annotation(com.ssm.annotation.DataSource)")
public void pointCut() {
}
@Before("pointCut() && @annotation(dataSource)")
public void doBefore(DataSource dataSource) {
String ds = dataSource.value().getValue();
MultipleDataSource.setDataSource(ds);
}
@After("pointCut()")
public void doAfter() {
MultipleDataSource.clear();
}
}
package com.ssm.multiple;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class MultipleDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();
@Override
protected Object determineCurrentLookupKey() {
return this.getDataSource();
}
/**
* 设置数据源
*
* @param db
*/
public static void setDataSource(String db) {
contextHolder.set(db);
}
/**
* 取得当前数据源
*
* @return
*/
public static String getDataSource() {
return contextHolder.get();
}
/**
* 清除上下文数据
*/
public static void clear() {
contextHolder.remove();
}
}
package com.ssm.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.ssm.enums.DataSourceEnum;
import com.ssm.multiple.MultipleDataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.ssm.mapper")
public class MyBatiesPlusConfiguration {
@Bean(name = "db1")
@ConfigurationProperties(prefix = "spring.datasource.druid.db1")
public DataSource db1() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "db2")
@ConfigurationProperties(prefix = "spring.datasource.druid.db2")
public DataSource db2() {
return DruidDataSourceBuilder.create().build();
}
/**
* 动态数据源配置
*
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("db1") DataSource db1, @Qualifier("db2") DataSource db2) {
MultipleDataSource multipleDataSource = new MultipleDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceEnum.DB1.getValue(), db1);
targetDataSources.put(DataSourceEnum.DB2.getValue(), db2);
//添加数据源
multipleDataSource.setTargetDataSources(targetDataSources);
//设置默认数据源
multipleDataSource.setDefaultTargetDataSource(db1);
return multipleDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
//添加分页插件
sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor()});
return sqlSessionFactory.getObject();
}
/**
* 分页插件,自动识别数据库类型
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
return paginationInterceptor;
}
}
上面已经配置好注解和多数据源的相关配置了,下面开始介绍注解的使用,因为注解定义时定义的使用范围是 ElementType.METHOD
,所以使用时在ServiceImpl
的方法上加上注解即可
package com.ssm.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ssm.annotation.DataSource;
import com.ssm.entity.Teacher;
import com.ssm.enums.DataSourceEnum;
import com.ssm.mapper.TeacherMapper;
import com.ssm.service.TeacherService;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.List;
@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherMapper, Teacher> implements TeacherService {
@Override
@DataSource(DataSourceEnum.DB2)
public boolean save(Teacher entity) {
return super.save(entity);
}
@Override
@DataSource(DataSourceEnum.DB2)
public boolean removeById(Serializable id) {
return super.removeById(id);
}
@Override
@DataSource(DataSourceEnum.DB2)
public boolean updateById(Teacher entity) {
return super.updateById(entity);
}
@Override
@DataSource(DataSourceEnum.DB2)
public List<Teacher> list(Wrapper<Teacher> queryWrapper) {
return super.list(queryWrapper);
}
@Override
@DataSource(DataSourceEnum.DB2)
public IPage<Teacher> page(IPage<Teacher> page, Wrapper<Teacher> queryWrapper) {
return super.page(page, queryWrapper);
}
}
其他的都是基本的ssm代码这里不再一一粘贴讲述了,道友们clone下项目一看就一目了然了
访问:http://127.0.0.1:81/ssm/student/list
访问:http://127.0.0.1:81/ssm/teacher/page?current=0&size=2
OK!多数据源生效,大功告成