您的当前位置:首页正文

mybatis-plus(多数据源配置)

2024-11-07 来源:个人技术集锦

1 依赖准备

引入mybatis-plus依赖:
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>

引入mysql依赖:
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>
</dependency>

<!-- mybatis-plus 多数据源 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>

2 配置准备

springboot启动类。配置@MapperScan注解,用于扫描Mapper文件位置:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@MapperScan("com.wjbgn.user.mapper")
@SpringBootApplication(@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}))
public class RobNecessitiesUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(RobNecessitiesUserApplication.class, args);
    }
}

数据源配置,此处配置一主一从的环境,当前我只有一台,所以此处配置一样的:
spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3306/rob_necessities?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai
          username: root
          password: 123456
        slave_1:
          url: jdbc:mysql://127.0.0.1:3306/rob_necessities?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai
          username: root
          password: 123456

补充:这里面因为默认使用的是HikariCP数据源,目前也推荐使用这个,相比于druid有更高的性能,但是不能忽略下面的配置,否则服务会不断抛出异常,原因是数据库的连接时常和连接池的配置没有做好。

spring:
  datasource:
    dynamic:
      hikari:
        max-lifetime: 1800000
        connection-timeout: 5000
        idle-timeout: 3600000
        max-pool-size: 12
        min-idle: 4
        connection-test-query: /**ping*/

3 多数据源

配置文件当中配置了主从的方式,其实mybatis-plus还支持更多的方式:
1、多主多从
spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master_1:
        master_2:
        slave_1:
        slave_2:
        slave_3:

2、多种数据库
spring:
  datasource:
    dynamic:
      primary: mysql #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        mysql:
        oracle:
        postgresql:
        h2:
        sqlserver:

3、混合配置
spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master_1:
        slave_1:
        slave_2:
        oracle_1:
        oracle_2:

4 @DS注解

可以注解在方法上或类上,同时存在就近原则 【方法上注解】 优先于 【类上注解】

@DS("slave_1")
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements IUserService {


    @DS("salve_1")
    @Override
    public List<UserDO> getList() {
        return this.getList();
    }

    @DS("master")
    @Override
    public int saveUser(UserDO userDO) {
        boolean save = this.save(userDO);
        if (save){
            return 1;
        }else{
            return 0;
        }
    }
}

当注解添加到类上,意味着此类里的方法都使用此数据源;
当注解添加到方法上时,意味着此方法上使用的数据源优先级高于其他一切配置;

1、错误方法如下(伪代码):
@Slf4j
@Service("serviceA")
public class ServiceImpl implements Service{
 
    /**
     * 实现方法A
     * @param 
     * @return
     */
    @Override
    @DS("database1")
    public void functionA() {
        ...
        functionB(); //调用B方法且切换为B方法的所需要的数据源
    }
 
    /**
     * 实现方法B
     * @param 
     * @return
     */
    @Override
    @DS("database2")
    public void function B() {
 
    }
}
如上图所示,A方法调用B方法时,B方法的@DS注解不生效,将完全被A方法的@DS("database1)所覆盖。
2、加入上@Transactiona注解方式:
@Transactional 是声明式事务管理 编程中使用的注解,实质是使用了 JDBC 的事务来进行事务控制的,基于Spring 的动态代理的机制
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
即可让Spring事务生效。
@Slf4j
@Service("serviceA")
public class ServiceImpl implements Service{
 
    /**
     * 实现方法A
     * @param 
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @DS("database1")
    public void functionA() {
        ...
        functionB(); //调用B方法且切换为B方法的所需要的数据源
    }
 
    /**
     * 实现方法B
     * @param 
     * @return
     */
    @Override
    @DS("database2")
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)  
    public void function B() {
 
    }
}

但是,重点来了!!!!方法B仍然未生效!依然被A方法的@DS("database1)所覆盖。

错误的根源在于:上面的方法只适用于在同一个类中不同方法不同数据源的使用,不能使用事务,否则数据源不会切换。

正确的方式:创建一个新类,调用不同类中的方法

注意仍需要加上@Transactional来让事务生效,如下图(伪代码)所示:
@Slf4j
@Service("serviceA")
public class ServiceImplA implements ServiceA{
    @Resource
    ServiceImplB serviceImplB ;
    /**
     * 实现方法A
     * @param 
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @DS("database1")
    public void functionA() {
        ...
        ServiceImplB.functionB(); //调用B类的B方法
    }
}
 
@Slf4j
@Service("serviceB")
public class ServiceImplB implements ServiceB{
      /**
     * 实现方法B
     * @param 
     * @return
     */
    @Override
    @DS("database2")
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)  
    public void function B() {
 
    }
}

最终B方法成功获取database2的数据源

显示全文