複数のデータソースを統合すると springboot がページネーションできないバグ + mybatis-plus

1. springboot は複数のデータソースを統合しています

1.1 依存関係の導入

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

1.2 ymlファイルの設定

次のステップでは、自動構成ではなく、yml ファイルの構成を手動で取得するため、コメントの下にあるさまざまなデータ ソースの名前に必ず注意してください。これら 2 つのデータベースは同時に使用できない可能性があるため、有効なスイッチを追加しました。これが true の場合、データベース構成がロードされます。

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 構成パッケージを作成します。SpringBoot がスキャンする限り、スタートアップ クラスに特別なコメントを追加する必要はありません。

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;

}

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関連の設定


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.MapperScan内部ではbasePackages、MyBatis が提供するマッパー インターフェイスの場所をスキャンします。sqlSessionTemplateRef の名前は任意であり、以降の注釈には影響しませんが、他の構成クラスの名前とは異なる必要があることに注意してください。

2. MysqlDataSourceConfig クラスの各メソッドの前にアノテーションがあり@Primary、PsqlDataSourceConfig クラスのメソッドは @Qualifier (これらのクラスが異なる限り、ここでの名前も任意です) になります。これは、SpringBoot が自動的にアセンブルされるのを防ぐためです。複数のデータ ソースがあるため、どれを使用すればよいか分からず、起動時にエラーが報告されます。データ ソースが 3 つ以上ある場合は、残りの構成クラス メソッドで PsqlDataSourceConfig のような @Qualifier アノテーションを使用します。

2. mybatis-plusが複数のデータソースでページネーションを実行できないバグを解決

問題:
プロジェクトで単一のデータ ソースを構成する場合、mybatis-plus ページング プラグインは正常に動作しますが、複数のデータ ソースを構成する場合、mybatis-plus ページング プラグインは失敗します。

2.1 mybatis-plus ページング プラグインを挿入する

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

2.2 複数のデータソースでの構成

 @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();
    }

複数のデータ ソースの構成は、デフォルトのプロパティ構成を使用するのではなく、コードによって実装する必要があります。ここでは、MybatisSqlSessionFactoryBean を使用して
SqlSessionFactory を構成します。MybatisSqlSessionFactoryBean には、プラグインの構成に使用される setPlugins: というメソッドがあります

MybatisSqlSessionFactoryBean Bean = new MybatisSqlSessionFactoryBean(); の使用は、パッケージをスキャンするときに mybatis-plus のマッパーがスキャンできない問題を解決するためです。

おすすめ

転載: blog.csdn.net/sunrj_niu/article/details/124469758