[Spring Boot] Spring Boot は複数のデータソースを統合します

序文

実際の開発作業では、複数のデータベースに同時に接続したり、読み取りと書き込みを分離したり、データベースをまたがってクエリを実行したりするなど、複数のデータソースを統合する必要がある状況に遭遇することがよくあります。この記事では、Spring Boot を使用して複数のデータソースの統合を実現する方法を紹介します。開発が初めての方にも役立ちます。

1. 基本的な考え方

1.1 複数のデータソースとは何ですか?

1 つのアプリケーションで複数のデータ ソースを使用するということは、異なるデータ ソースからデータを取得するために、異なるデータ ソース間で切り替える必要があることを意味します。複数のデータ ソースには、リレーショナル データベース、NoSQL データベース、フラット ファイル、XML ファイルなどがあります。アプリケーションで複数のデータ ソースを使用する利点は、アプリケーションのニーズに応じて最適なデータ ソースを選択できるため、アプリケーションのパフォーマンスとスケーラビリティが向上することです。

1.2 複数のデータ ソースを使用する理由は何ですか?

アプリケーションで複数のデータ ソースを使用することの利点は数多くありますが、その一部を次に示します。

  • アプリケーションのニーズに応じて最適なデータ ソースを選択できるため、アプリケーションのパフォーマンスとスケーラビリティが向上します。
  • 単一障害点など、単一のデータ ソースによってもたらされるリスクを軽減できます。
  • データ分離とデータ セキュリティをより適切にサポートできます。
  • さまざまなビジネス ニーズをより適切にサポートできます。

2. Spring Boot で複数のデータソースを統合するにはどうすればよいですか?

2.1 基本構成

次の統合例では、実際のプロジェクトの統合例を紹介します。プロジェクトにはmysqlとphoenixの複数のデータソースがあります。その後、詳細に説明します。プロジェクトにはMybatisPlusが統合されているため、関連する設定もいくつかあります。まず、対応する依存関係を pom.xml ファイルに追加する必要があります。一般的に使用されるデータ ソースの依存関係は次のとおりです。

<!-- 数据源 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL 数据库 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- phoenix 数据库 -->
<dependency>
    <groupId>org.apache.phoenix</groupId>
    <artifactId>phoenix-core</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>        
</dependency>

次に、application.properties または application.yml ファイルで各データ ソースの接続情報を構成する必要があります。構成例を次に示します。

 #数据库配置
  datasource:
    #mysql数据源配置
    mysql:
      type: com.zaxxer.hikari.HikariDataSource
      jdbc-url: jdbc:p6spy:mysql://xxxx:3306/xxxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
      username: xxxx
      password: xxxxx
      # Hikari 连接池配置
      # 最小空闲连接数量
      hikari:
        minimum-idle: 5
        # 空闲连接存活最大时间,默认600000(10分钟)
        idle-timeout: 180000
        # 连接池最大连接数,默认是10
        maximum-pool-size: 10
        # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
        auto-commit: true
        # 连接池名称
        pool-name: xxxx-HikariCP
        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
        max-lifetime: 1800000
        # 数据库连接超时时间,默认30秒,即30000
        connection-timeout: 30000
        connection-test-query: SELECT 1

    #phoenix数据源配置
    phoenix:
      driver: org.apache.phoenix.jdbc.PhoenixDriver
      jdbcUrl: jdbc:phoenix:xxxxx:xxxx
      user:
      password:
      connectionProperties:
      isNamespaceMappingEnabled: true
      mapSystemTablesToNamespace: true
      schema: 'xxxxxx'
      maximumPoolSize: 128
      maxLifetime: 1200000

その後、複数のデータ ソースの構成クラスといくつかの基本的な構成を作成する必要があります。サンプルコードは次のとおりです。

@EnableTransactionManagement
@Configuration
@MapperScan("com.xxxx.mapper")
public class DataSourceConfig {
    
    

    @Autowired
    private PhoenixProperties phoenixProperties;

    @Bean
    public PaginationInterceptor paginationInterceptor() {
    
    
        return new PaginationInterceptor().setDialectType("mysql");
    }


    @Bean(name = "mysqlDateSource")
    @ConfigurationProperties(prefix = "spring.datasource.mysql")
    public HikariDataSource mysqlDateSource() {
    
    
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "phoenixDataSource")
    public HikariDataSource phoenixDataSource() {
    
    
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(phoenixProperties.getDriver());
        dataSource.setJdbcUrl(phoenixProperties.getJdbcUrl());
        dataSource.setUsername(phoenixProperties.getUser());
        dataSource.setPassword(phoenixProperties.getPassword());
        Properties properties = new Properties();
        properties.setProperty("phoenix.schema.isNamespaceMappingEnabled", phoenixProperties.getIsNamespaceMappingEnabled());
        properties.setProperty("phoenix.schema.mapSystemTablesToNamespace", phoenixProperties.getMapSystemTablesToNamespace());
        properties.setProperty("schema", phoenixProperties.getSchema());
        properties.setProperty("maximum-pool-size", phoenixProperties.getMaximumPoolSize());
        properties.setProperty("max-lifetime", phoenixProperties.getMaxLifetime());
        dataSource.setDataSourceProperties(properties);
        return dataSource;
    }

    /**
     * 动态数据源配置
     *
     * @return DataSource
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier("mysqlDateSource") DataSource mysqlDateSource, @Qualifier("phoenixDataSource") DataSource phoenixDataSource) {
    
    
        MultipleDataSource multipleDataSource = new MultipleDataSource();
        Map<Object, Object> targetDataSources = Maps.newHashMap();
        targetDataSources.put(DataSourceEnum.MYSQL.getValue(), mysqlDateSource);
        targetDataSources.put(DataSourceEnum.PHOENIX.getValue(), phoenixDataSource);
        //添加数据源
        multipleDataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源
        multipleDataSource.setDefaultTargetDataSource(mysqlDateSource);
        return multipleDataSource;
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
    
    
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(mysqlDateSource(), phoenixDataSource()));
        ResourceLoader loader = new DefaultResourceLoader();
        String resource = "classpath:mybatis-config.xml";
        sqlSessionFactory.setConfigLocation(loader.getResource(resource));
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactory.setMapperLocations(resolver.getResources("classpath*:/mapper/*.xml"));
        sqlSessionFactory.setTypeAliasesPackage("com.huitongjy.oms.web.entity");
        sqlSessionFactory.setTypeEnumsPackage("com.huitongjy.oms.common.enums");
        //关键代码 设置 MyBatis-Plus 分页插件
        Interceptor[] plugins = {
    
    paginationInterceptor()};
        sqlSessionFactory.setPlugins(plugins);
        return sqlSessionFactory.getObject();
    }

}

MultipleDataSource クラス

public class MultipleDataSource extends AbstractRoutingDataSource {
    
    

    @Override
    protected Object determineCurrentLookupKey() {
    
    
        return DataSourceContextHolder.getDataSource();
    }
}

class DataSourceContextHolder {
    
    

    private static final ThreadLocal<String> CONTEXT_HOLDER = new InheritableThreadLocal<>();

    /**
     * 设置数据源
     *
     * @param db String
     */
    static void setDataSource(String db) {
    
    
        CONTEXT_HOLDER.set(db);
    }

    /**
     * 取得当前数据源
     *
     * @return String
     */
    static String getDataSource() {
    
    
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清除上下文数据
     */
    static void clear() {
    
    
        CONTEXT_HOLDER.remove();
    }
}

mybatis-config.xml の構成は次のとおりです。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <setting name="cacheEnabled" value="false"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
    <setting name="useColumnLabel" value="true"/>
    <setting name="useGeneratedKeys" value="false"/>
    <setting name="autoMappingBehavior" value="PARTIAL"/>
    <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
    <setting name="defaultExecutorType" value="SIMPLE"/>
    <setting name="defaultStatementTimeout" value="25"/>
    <setting name="defaultFetchSize" value="100"/>
    <setting name="safeRowBoundsEnabled" value="false"/>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <setting name="localCacheScope" value="SESSION"/>
    <setting name="jdbcTypeForNull" value="OTHER"/>
    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
  </settings>
</configuration>

上記のコードでは、 @ConfigurationProperties(prefix = "spring.datasource.mysql") を使用して、構成ファイル内のプロパティを mysql の HikariDataSource オブジェクトにバインドします。同様に、phoenixDataSource にもバインドします。
これら 2 つのデータ ソースはそれぞれ異なるデータベースに接続されています。@Primary アノテーションはプライマリ データ ソースを表し、実行時にどのデータ ソースを使用するかを決定するために使用されます。データ ソースを追加し、既定のデータ ソースを設定する multipleDataSource というメソッドを作成しました。
最後に、データベース接続を管理および構成できるように、複数のデータ ソースといくつかの構成ファイルが SqlSessionFactory に読み込まれます。

2.2 アイテムコード

設定後は、phoenix インターフェイスでデータ ソースを設定するだけです。コードは次のとおりです。

public interface TestDetailMapper {
    
    

    /**
     * 保存
     * @param testDetail
     */
    @DataSource(DataSourceEnum.PHOENIX)
    @Insert(" upsert into xxx_xxx (xxxxx) " +
        " values (#{xxxx})")
    void insert(@Param("xxx") TestDetail testDetail);


    /**
     * 获取内容
     *
     * @param xxxx
     * @return xxx
     */
    @Results(value = {
    
    
        @Result(property = "xxxx", column = "xxxxx")
    })
    @DataSource(DataSourceEnum.PHOENIX)
    @Select("SELECT XXXX FROM XXX WHERE XXX=#{XXXX}")
    TestDetail findByKey(@Param("XXXX") String XXXX);

}

上記のコードにより、複数のデータソースの統合を実現することができました。

2.3 注意事項

(1) プライマリ データ ソースを指定する必要があります。つまり、プライマリ データ ソースの Bean インジェクションに @Primary アノテーションを追加します。これは、同じクラスの複数のオブジェクトがインジェクトされ、@Primary アノテーションを持つオブジェクトが優先されることを示します。 。
(2) 構成ファイルのマッピングを個別に構成できるように、異なるデータ ソースの dao ファイルとマッパー ファイルを異なるパッケージ パスとファイル パスに分離することが最善です。そうしないと、エラーが発生します。

おすすめ

転載: blog.csdn.net/u011397981/article/details/132465702