SpringBoot+mybatisPlus实现多数据源curd
适用场景:一个项目需要连接多个数据库的时候
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x。
示例项目 可参考项目下的samples目录。
官方文档
https://mybatis.plus/guide/dynamic-datasource.html
官方文档进阶版
https://github.com/baomidou/dynamic-datasource-spring-boot-starter/wiki
官方约定
- 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
- 配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
- 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
- 默认的数据源名称为 master ,你可以通过spring.datasource.dynamic.primary 修改。
- 方法上的注解优先于类上注解。
- 强烈建议只在service的类和方法上添加注解,不建议在mapper上添加注解。
核心注解为:@DS("YAML-DBNAME")
一、使用方法
1、代码目录结构
2、POM文件
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.microsoft.sqlserver/sqljdbc4 -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.0</version>
</dependency>
3、application.yml文件
- 主数据库为mysql,从数据库为sqlServer
server:
port: 8002
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
datasource:
master:
url: jdbc:mysql://192.168.101.28:3306/smart_meal_order?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&tinyInt1isBit=false&allowMultiQueries=true
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
sqlserver:
url: jdbc:sqlserver://192.168.101.28:1433;databaseName=jxs2
username: sa
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
mybatis-plus:
# mapper-locations: classpath:/mapper/*.xml,classpath:/other/*.xml
global-config:
db-config:
#驼峰下划线转换
table-underline: true
configuration:
##配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,sql需要写as:select user_id as userId)
map-underscore-to-camel-case: true
cache-enabled: true #配置的缓存的全局开关
lazyLoadingEnabled: true #延时加载的开关
multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性
logging:
level.cn.com.smart: debug
4、SqlServer.java
这个类是个自定义注解类,封装@DS()用的
package cn.com.smart.annotation;
import com.baomidou.dynamic.datasource.annotation.DS;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@DS("sqlserver")
public @interface SqlServer {
}
- 接下来如果要切换sqlserver数据库,只需要在需要切换的类上加上注解@SqlServer即可
5、在需要切换库的类上加注解
- 官方建议加在Service上
示例: - curd sqlServer数据库的代码JxOrgService
@Service
@Slf4j
@SqlServer
public class JxOrgService {
@Autowired
private JxOrgMapper jxOrgMapper;
/**
* 从sqlserver这个表查出所有数据
* @return
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public List<JxOrg> select() {
List<JxOrg> jxOrgs = jxOrgMapper.selectList(null);
return jxOrgs;
}
}
- curd Mysql数据库的代码OrgService
@Service
@Slf4j
public class OrgService extends ServiceImpl<OrgMapper,Org> implements IService<Org> {
@Autowired
private OrgMapper orgMapper;
@Autowired
private JxOrgService jxOrgService;
/**
* 从MySQL查这个表的所有
* @return
*/
public List<Org> select(){
List<Org> orgs = orgMapper.selectList(null);
if (orgs != null && orgs.size() > 0) {
log.info("mysql_orgid" + String.valueOf(orgs.get(0).getId()));
}
return orgs;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public boolean insertBatch(){
List<JxOrg> jxOrgLists = jxOrgService.select();
ArrayList<Org> orgs = new ArrayList<>();
if (jxOrgLists!=null&&jxOrgLists.size()>0){
for (JxOrg jxOrg : jxOrgLists){
Org org = new Org();
org.setId(String.valueOf(jxOrg.getOrgId()));
org.setName(jxOrg.getName());
org.setRemark(jxOrg.getName());
org.setIsEnable(1);
org.setIsDeleted(0);
org.setCreateTime(new Date());
org.setModifierId(String.valueOf(1));
org.setModifyTime(new Date());
orgs.add(org);
}
}
//批量插入方法
boolean b = saveBatch(orgs);
//如果超过1000条了就多加个参数
//saveBatch(orgs,2000);
return b;
}
}
可以看到查master的mysql数据库的OrgService类没有加@SqlServer的注解,而查other的sqlserver数据库的JxOrgService类加了@SqlServer的注解。此时就已经实现了分库查询
二、遇到的坑
以下说明在开发时遇到的坑
1、注解@DS和@Transactional冲突问题
- 问题描述:
如果service上同时加上这两个注解,且在这个service类里出现了切换数据库的方法,就会报错没有找到相应的表,因为没有切库成功
不可以
- 解决方案
1、去掉两个service类上的@Transactional注解
2、在被引用的方法和需要切库的方法上单独加上注解
3、并且修改@Transactional的属性
@Transactional(propagation = Propagation.REQUIRES_NEW)
-
此时切换库就成功了
-
这里借鉴了csdn的文章,他把栈都分析了
https://blog.csdn.net/Onstduy/article/details/106093994 -
延申知识
spring的事务传播机制
https://blog.csdn.net/weixin_40378837/article/details/104715157
PROPAGATION_REQUIRED (默认)
- 支持当前事务,如果当前没有事务,则新建事务
- 如果当前存在事务,则加入当前事务,合并成一个事务
REQUIRES_NEW (一般用在子方法需要单独事务)
- 新建事务,如果当前存在事务,则把当前事务挂起
- 这个方法会独立提交事务,不受调用者的事务影响,父级异常,它也是正常提交