SpringBoot+mybatisPlus实现多数据源

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、代码目录结构

image-20200808134817072

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类里出现了切换数据库的方法,就会报错没有找到相应的表,因为没有切库成功

image-20200808141126570
不可以
  • 解决方案
    1、去掉两个service类上的@Transactional注解
    2、在被引用的方法和需要切库的方法上单独加上注解
    3、并且修改@Transactional的属性
@Transactional(propagation = Propagation.REQUIRES_NEW)

image-20200808141318815

image-20200808141444760

PROPAGATION_REQUIRED (默认)

  • 支持当前事务,如果当前没有事务,则新建事务
  • 如果当前存在事务,则加入当前事务,合并成一个事务

REQUIRES_NEW (一般用在子方法需要单独事务)

  • 新建事务,如果当前存在事务,则把当前事务挂起
  • 这个方法会独立提交事务,不受调用者的事务影响,父级异常,它也是正常提交

Q.E.D.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水豚少年的码农生活

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值