The BUG that springboot cannot paginate when integrating multiple data sources + mybatis-plus

1. springboot is integrating multiple data sources

1.1 Introducing dependencies

<dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

1.2 Configure the yml file

Be sure to pay attention to the names of the different data sources below the comments, because the next step is to manually obtain the configuration of the yml file, not automatic configuration. Because these two databases may not be used at the same time, I added the enabled switch. If it is true, the database configuration will be loaded

spring:
  #mysql数据源
  mysqldb:
    enabled: false
    driver-class-name: com.mysql.cj.jdbc.Driver
    password: root
    url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&useUnicode=true&serverTimezone=Asia/Shanghai&allowMultiQueries=true&useAffectedRows=true&useSSL=false
    username: root
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    filters: stat,wall,log4j
    initialSize: 5
    maxActive: 20
    maxPoolPreparedStatementPerConnectionSize: 20
    maxWait: 60000
    minEvictableIdleTimeMillis: 300000
    minIdle: 5
    poolPreparedStatements: true
    testOnBorrow: false
    testOnReturn: false
    testWhileIdle: true
    timeBetweenEvictionRunsMillis: 60000
    validationQuery: SELECT 1 FROM DUAL
  #psql数据源
  psqldb:
    enabled: true
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://127.0.0.1:9543/test
    username: postgres
    password: postgres
    # 初始化大小,最小,最大
    initialSize: 1
    minIdle: 3
    maxActive: 200
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 40000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false

1.3 Create a config package, as long as SpringBoot scans it, there is no need to add special comments on the startup class

Related configuration of mysql

import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;


@Component
@Data
@ConfigurationProperties(prefix = "spring.mysqldb")
public class MysqlDataSourceProperties implements Serializable {
    
    

    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private String connectionProperties;
    private String filters;
    private Integer initialSize;
    private Integer maxActive;
    private Integer maxPoolPreparedStatementPerConnectionSize;
    private Integer maxWait;
    private Integer minEvictableIdleTimeMillis;
    private Integer minIdle;
    private boolean poolPreparedStatements;
    private boolean testOnBorrow;
    private boolean testOnReturn;
    private boolean testWhileIdle;
    private Integer timeBetweenEvictionRunsMillis;
    private String validationQuery;
    private boolean enabled;

}

load mysql


import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.util.List;

@Configuration
## ConditionalOnProperty指定enabled开启时,才会加载该数据库相关信息
@ConditionalOnProperty(prefix = "spring.mysqldb", name = "enabled", havingValue = "true")
@MapperScan(basePackages = "com.koal.ipsec.mapper", sqlSessionFactoryRef = "mysqlSqlSessionFactory")
public class MysqlDataSourceConfig {
    
    

    @Autowired
    private MysqlDataSourceProperties mysqlDataSourceProperties;

    @Bean(name = "mysqlDataSource")
    public DataSource dataSource() {
    
    
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(mysqlDataSourceProperties.getDriverClassName());
        dataSource.setUrl(mysqlDataSourceProperties.getUrl());
        dataSource.setUsername(mysqlDataSourceProperties.getUsername());
        dataSource.setPassword(mysqlDataSourceProperties.getPassword());
        dataSource.setInitialSize(mysqlDataSourceProperties.getInitialSize());
        dataSource.setMinIdle(mysqlDataSourceProperties.getMinIdle());
        dataSource.setMaxActive(mysqlDataSourceProperties.getMaxActive());
        return dataSource;
    }

    @Bean
    public PaginationInterceptor paginationInterceptor() {
    
    
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDialectType(DbType.MYSQL.getDb());
        return paginationInterceptor;
    }

    @Bean(name = "mysqlSqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
    
    
      ##注意: Mybatis plus分页插件配置必须使用MybatisSqlSessionFactoryBean ,否则分页失效
      ## MybatisSqlSessionFactoryBean 是为了解决扫包的时候无法扫描mybatis-plus的mapper的问题。
        final MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
       ## 重点
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisSqlSessionFactoryBean.setConfiguration(mybatisConfiguration);
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        mybatisSqlSessionFactoryBean.setPlugins(new Interceptor[]{
    
    paginationInterceptor()});
        ## 指定enum类的扫描
        mybatisSqlSessionFactoryBean.setTypeEnumsPackage("com.test.constant");
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resourcePatternResolver.getResources("classpath*:mapper/*Mapper.xml");
        mybatisSqlSessionFactoryBean.setMapperLocations(resources);
        return mybatisSqlSessionFactoryBean.getObject();
    }
    @Bean(name = "mysqlSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
    
    
        return new SqlSessionTemplate(sqlSessionFactory);
    }
    @Bean(name = "mysqlTransactionManager")
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("mysqlDataSource") DataSource dataSource) {
    
    
        return new DataSourceTransactionManager(dataSource);
    }

}

psql related configuration


import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Component
@Data
@ConfigurationProperties(prefix = "spring.psqldb")
public class PsqlDataSourceProperties implements Serializable {
    
    

    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private Integer initialSize;
    private Integer maxActive;
    private Integer maxWait;
    private Integer minIdle;
    private Integer timeBetweenEvictionRunsMillis;
    private Integer minEvictableIdleTimeMillis;
    private String validationQuery;
    private boolean testOnBorrow;
    private boolean testOnReturn;
    private boolean testWhileIdle;

}


import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
@ConditionalOnProperty(prefix = "spring.psqldb", name = "enabled", havingValue = "true")
@MapperScan(basePackages = "com.koal.ipsec.mapper", sqlSessionFactoryRef = "psqlSqlSessionFactory")
public class PsqlDataSourceConfig {
    
    

    @Autowired
    private PsqlDataSourceProperties psqlDataSourceProperties;

    @Bean(name = "psqlDataSource")
    public DataSource dataSource() {
    
    
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(psqlDataSourceProperties.getDriverClassName());
        dataSource.setUrl(psqlDataSourceProperties.getUrl());
        dataSource.setUsername(psqlDataSourceProperties.getUsername());
        dataSource.setPassword(psqlDataSourceProperties.getPassword());
        dataSource.setInitialSize(psqlDataSourceProperties.getInitialSize());
        dataSource.setMinIdle(psqlDataSourceProperties.getMinIdle());
        dataSource.setMaxActive(psqlDataSourceProperties.getMaxActive());
        return dataSource;
    }

    @Bean
    public PaginationInterceptor paginationInterceptor() {
    
    
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDialectType(DbType.POSTGRE_SQL.getDb());
        return paginationInterceptor;
    }

    @Bean(name = "psqlSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("psqlDataSource") DataSource dataSource) throws Exception {
    
    
        final MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisSqlSessionFactoryBean.setConfiguration(mybatisConfiguration);
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        mybatisSqlSessionFactoryBean.setPlugins(new Interceptor[]{
    
    paginationInterceptor()});
       // mybatisSqlSessionFactoryBean.setPlugins(new Interceptor[]{
    
    psqlMybatisPlusConfig.paginationInterceptor()});
        mybatisSqlSessionFactoryBean.setTypeEnumsPackage("com.koal.ipsec.constant");
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resourcePatternResolver.getResources("classpath*:mapper/*Mapper.xml");
        mybatisSqlSessionFactoryBean.setMapperLocations(resources);
        return mybatisSqlSessionFactoryBean.getObject();
    }
    @Bean(name = "psqlSqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("psqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
    
    
        return new SqlSessionTemplate(sqlSessionFactory);
    }
    @Bean(name = "psqlTransactionManager")
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("psqlDataSource") DataSource dataSource) {
    
    
        return new DataSourceTransactionManager(dataSource);
    }



}

1. MapperScanInside basePackagesis to scan the location of the mapper interface provided by MyBatis. The name in sqlSessionTemplateRef is arbitrary and has no effect on the following annotations, but note that it should be different from the names of the other configuration classes.

2. There are annotations in front of each method of the MysqlDataSourceConfig class @Primary, and the method of the PsqlDataSourceConfig class becomes @Qualifier (the name here is also arbitrary, as long as these classes are different), this is to prevent SpringBoot from automatically assembling. Multiple data sources, do not know which one to use and report an error at startup. If there are more than two data sources, use the @Qualifier annotation like PsqlDataSourceConfig on the rest of the configuration class methods.

2. Solve the bug that multiple data sources mybatis-plus cannot perform pagination

Problem:
When the project configures a single data source, the mybatis-plus paging plugin works normally. When configuring multiple data sources, the mybatis-plus paging plugin fails.

2.1 Inject mybatis-plus paging plugin

   @Bean
    public PaginationInterceptor paginationInterceptor() {
    
    
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDialectType(DbType.POSTGRE_SQL.getDb());
        return paginationInterceptor;
    }

2.2 Configuration under multiple data sources

 @Bean(name = "mysqlSqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
    
    
      ##注意: Mybatis plus分页插件配置必须使用MybatisSqlSessionFactoryBean ,否则分页失效
      ## MybatisSqlSessionFactoryBean 是为了解决扫包的时候无法扫描mybatis-plus的mapper的问题。
        final MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
       ## 重点
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisSqlSessionFactoryBean.setConfiguration(mybatisConfiguration);
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        mybatisSqlSessionFactoryBean.setPlugins(new Interceptor[]{
    
    paginationInterceptor()});
        ## 指定enum类的扫描
        mybatisSqlSessionFactoryBean.setTypeEnumsPackage("com.test.constant");
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resourcePatternResolver.getResources("classpath*:mapper/*Mapper.xml");
        mybatisSqlSessionFactoryBean.setMapperLocations(resources);
        return mybatisSqlSessionFactoryBean.getObject();
    }

The configuration of multiple data sources needs to be implemented by code instead of using the default properties configuration. Here I use
MybatisSqlSessionFactoryBean to configure SqlSessionFactory, and MybatisSqlSessionFactoryBean just has a method that is
setPlugins: used to configure plugins.

The use of MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean(); is to solve the problem that the mapper of mybatis-plus cannot be scanned when scanning packages.

Guess you like

Origin blog.csdn.net/sunrj_niu/article/details/124469758