ホームページ Java &#&チュートリアル Spring mybatisの複数のデータソースインスタンスの詳細な説明

Spring mybatisの複数のデータソースインスタンスの詳細な説明

Jan 24, 2017 am 10:24 AM

同じプロジェクトに複数のデータベース、つまり複数のデータ ソースが含まれる場合があります。複数のデータ ソースは 2 つの状況に分類できます:

1) 2 つ以上のデータベースは関連しておらず、独立しています。実際、これは 2 つのプロジェクトとして開発できます。たとえば、ゲーム開発では、1 つのデータベースがプラットフォーム データベースであり、そのプラットフォームの下にゲームに対応する別のデータベースが存在します。 2) 2 つ以上のデータベースがマスターとスレーブの関係にある場合。 -master は mysql で構築され、その後、複数のスレーブが存在します。または、MHA を使用してマスターとスレーブのレプリケーションを構築します

現在、私が知っている Spring の複数のデータ ソースを構築するには 2 つの方法があり、選択できます。複数のデータソースの状況に応じて。

1. Spring 構成ファイルを使用して複数のデータ ソースを直接構成します

たとえば、2 つのデータベースが関連していない場合は、次のように Spring 構成ファイルで複数のデータ ソースを直接構成し、トランザクションを個別に構成できます。表示:

<context:component-scan base-package="net.aazj.service,net.aazj.aop" />
<context:component-scan base-package="net.aazj.aop" />
<!-- 引入属性文件 -->
<context:property-placeholder location="classpath:config/db.properties" />
  
<!-- 配置数据源 -->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <property name="url" value="${jdbc_url}" />
  <property name="username" value="${jdbc_username}" />
  <property name="password" value="${jdbc_password}" />
  <!-- 初始化连接大小 -->
  <property name="initialSize" value="0" />
  <!-- 连接池最大使用连接数量 -->
  <property name="maxActive" value="20" />
  <!-- 连接池最大空闲 -->
  <property name="maxIdle" value="20" />
  <!-- 连接池最小空闲 -->
  <property name="minIdle" value="0" />
  <!-- 获取连接最大等待时间 -->
  <property name="maxWait" value="60000" />
</bean>
  
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
 <property name="configLocation" value="classpath:config/mybatis-config.xml" />
 <property name="mapperLocations" value="classpath*:config/mappers/**/*.xml" />
</bean>
  
<!-- Transaction manager for a single JDBC DataSource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>
  
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
  
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 <property name="basePackage" value="net.aazj.mapper" />
 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
  
<!-- Enables the use of the @AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy/>
ログイン後にコピー

2 番目のデータ ソースの構成

<bean name="dataSource_2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <property name="url" value="${jdbc_url_2}" />
  <property name="username" value="${jdbc_username_2}" />
  <property name="password" value="${jdbc_password_2}" />
  <!-- 初始化连接大小 -->
  <property name="initialSize" value="0" />
  <!-- 连接池最大使用连接数量 -->
  <property name="maxActive" value="20" />
  <!-- 连接池最大空闲 -->
  <property name="maxIdle" value="20" />
  <!-- 连接池最小空闲 -->
  <property name="minIdle" value="0" />
  <!-- 获取连接最大等待时间 -->
  <property name="maxWait" value="60000" />
</bean>
  
<bean id="sqlSessionFactory_slave" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource_2" />
 <property name="configLocation" value="classpath:config/mybatis-config-2.xml" />
 <property name="mapperLocations" value="classpath*:config/mappers2/**/*.xml" />
</bean>
  
<!-- Transaction manager for a single JDBC DataSource -->
<bean id="transactionManager_2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource_2" />
</bean>
  
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager_2" />
  
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 <property name="basePackage" value="net.aazj.mapper2" />
 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_2"/>
</bean>
ログイン後にコピー

上に示すように、2 つの dataSource、2 つの sqlSessionFactory、2 つのtransactionManager を構成しました。重要な点は MapperScannerConfigurer 構成です。 lSessionFactoryBeanName 属性異なる sqlSessionFactory の名前を注入します。この場合、対応する sqlSessionFactory が異なるデータベースに対応するマッパー インターフェイスに注入されます。

この複数のデータベースの構成は分散トランザクションをサポートしていないことに注意してください。つまり、複数のデータベースを同じトランザクションで操作することはできません。この構成方法の利点は、非常にシンプルであることですが、柔軟性が低いことです。特に柔軟性が必要なマスター・スレーブ型のマルチデータソース構成には適しておらず、業種に応じた細かな設定が必要となります。たとえば、特に時間がかかる一部の選択ステートメントについては、スレーブで実行したいと考えていますが、更新や削除などの操作はマスターでのみ実行できます。また、高い実数を必要とする一部の選択ステートメントについては、たとえば、ショッピングモールに武器を購入するシナリオでは、購入が完了した後、同時に購入操作をマスターで実行する必要があります。 、所有している武器と金貨を再クエリする必要がある場合、スレーブでは遅延が発生する可能性があるため、このクエリはスレーブではなくマスターで実行されないようにする必要があるかもしれません。購入が成功した後、バックパックの中で武器が見つからないことにプレイヤーが気づいてほしいと考えています。

そのため、マスター・スレーブ型のマルチデータソースの構成では、どの選択をスレーブに配置してどの選択をスレーブに配置できないかを、業務に応じて柔軟に設定する必要があります。したがって、上記のデータソース構成は適切ではありません。

2. AbstractRoutingDataSource と AOP に基づく複数のデータ ソースの構成

基本原則は、DataSource クラス ThreadLocalRountingDataSource を独自に定義して AbstractRoutingDataSource を継承し、マスター データ ソースとスレーブ データ ソースを構成ファイルの ThreadLocalRountingDataSource に挿入することです。次に、パス AOP を使用して、マスター データ ソースを選択する場所とスレーブ データ ソースを選択する場所を柔軟に構成します。コードの実装を見てみましょう:

1) まず、さまざまなデータ ソースを表す列挙型を定義します:

   
package net.aazj.enums;
  
/**
 * 数据源的类别:master/slave
 */
public enum DataSources {
  MASTER, SLAVE
}
ログイン後にコピー

2) TheadLocal を使用して、各スレッドが選択するデータ ソースのキー (キー) を保存します:

package net.aazj.util;
  
import net.aazj.enums.DataSources;
  
public class DataSourceTypeManager {
  private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>(){
    @Override
    protected DataSources initialValue(){
      return DataSources.MASTER;
    }
  };
    
  public static DataSources get(){
    return dataSourceTypes.get();
  }
    
  public static void set(DataSources dataSourceType){
    dataSourceTypes.set(dataSourceType);
  }
    
  public static void reset(){
    dataSourceTypes.set(DataSources.MASTER0);
  }
}
ログイン後にコピー

3) ThreadLocalRountingDataSource を定義し、AbstractRoutingDataSource を継承します:

package net.aazj.util;
  
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  
public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {
  @Override
  protected Object determineCurrentLookupKey() {
    return DataSourceTypeManager.get();
  }
}
ログイン後にコピー

4) マスター データ ソースとスレーブ データ ソースを構成ファイルの ThreadLocalRountingDataSource に挿入します:

<context:component-scan base-package="net.aazj.service,net.aazj.aop" />
<context:component-scan base-package="net.aazj.aop" />
<!-- 引入属性文件 -->
<context:property-placeholder location="classpath:config/db.properties" /> 
<!-- 配置数据源Master -->
<bean name="dataSourceMaster" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <property name="url" value="${jdbc_url}" />
  <property name="username" value="${jdbc_username}" />
  <property name="password" value="${jdbc_password}" />
  <!-- 初始化连接大小 -->
  <property name="initialSize" value="0" />
  <!-- 连接池最大使用连接数量 -->
  <property name="maxActive" value="20" />
  <!-- 连接池最大空闲 -->
  <property name="maxIdle" value="20" />
  <!-- 连接池最小空闲 -->
  <property name="minIdle" value="0" />
  <!-- 获取连接最大等待时间 -->
  <property name="maxWait" value="60000" />
</bean> 
<!-- 配置数据源Slave -->
<bean name="dataSourceSlave" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <property name="url" value="${jdbc_url_slave}" />
  <property name="username" value="${jdbc_username_slave}" />
  <property name="password" value="${jdbc_password_slave}" />
  <!-- 初始化连接大小 -->
  <property name="initialSize" value="0" />
  <!-- 连接池最大使用连接数量 -->
  <property name="maxActive" value="20" />
  <!-- 连接池最大空闲 -->
  <property name="maxIdle" value="20" />
  <!-- 连接池最小空闲 -->
  <property name="minIdle" value="0" />
  <!-- 获取连接最大等待时间 -->
  <property name="maxWait" value="60000" />
</bean> 
<bean id="dataSource" class="net.aazj.util.ThreadLocalRountingDataSource">
  <property name="defaultTargetDataSource" ref="dataSourceMaster" />
  <property name="targetDataSources">
    <map key-type="net.aazj.enums.DataSources">
      <entry key="MASTER" value-ref="dataSourceMaster"/>
      <entry key="SLAVE" value-ref="dataSourceSlave"/>
      <!-- 这里还可以加多个dataSource -->
    </map>
  </property>
</bean> 
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
 <property name="configLocation" value="classpath:config/mybatis-config.xml" />
 <property name="mapperLocations" value="classpath*:config/mappers/**/*.xml" />
</bean> 
<!-- Transaction manager for a single JDBC DataSource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean> 
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 <property name="basePackage" value="net.aazj.mapper" />
 <!-- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> -->
</bean>
ログイン後にコピー

上記の Spring 構成ファイルでは、マスターをターゲットとしています。データベースデータベースは、それぞれ dataSourceMaster と dataSourceSlave の 2 つの dataSource を定義し、それらを に挿入して、異なるデータソースに基づいて dataSource を選択できるようにします。データソースマスターとデータソーススレーブのキー。

5) Spring AOP を使用して dataSource のキーを指定し、dataSource がそのキーに基づいて dataSourceMaster と dataSourceSlave を選択するようにします。 @Pointcut(" 実行中のメソッド(public * net.aazj.service..*.getUser(..))") が呼び出される前に、DataSourceTypeManager.set(DataSources.SLAVE) を呼び出してキー タイプを DataSources.SLAVE に設定します。 , したがって、dataSource は key=DataSources.SLAVE に基づいて dataSourceSlave dataSource を選択します。したがって、このメソッドの SQL ステートメントはスレーブ データベースで実行されます。

DataSourceInterceptor アスペクトを拡張し続け、その中でさまざまな定義を作成して、サービスのメソッドに対応する適切なデータ ソースを指定できます。

このように、Spring AOP の強力な機能を使用して、非常に柔軟に構成できます。

6) AbstractRoutingDataSource の原理の分析

ThreadLocalRountingDataSource 继承了 AbstractRoutingDataSource, 实现其抽象方法 protected abstract Object determineCurrentLookupKey(); 从而实现对不同数据源的路由功能。我们从源码入手分析下其中原理:

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean
AbstractRoutingDataSource 实现了 InitializingBean 那么spring在初始化该bean时,会调用InitializingBean的接口
void afterPropertiesSet() throws Exception; 我们看下AbstractRoutingDataSource是如何实现这个接口的:
  
  @Override
  public void afterPropertiesSet() {
    if (this.targetDataSources == null) {
      throw new IllegalArgumentException("Property &#39;targetDataSources&#39; is required");
    }
    this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
    for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
      Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
      DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
      this.resolvedDataSources.put(lookupKey, dataSource);
    }
    if (this.defaultTargetDataSource != null) {
      this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
    }
  }
ログイン後にコピー

targetDataSources 是我们在xml配置文件中注入的 dataSourceMaster 和 dataSourceSlave. afterPropertiesSet方法就是使用注入的。

dataSourceMaster 和 dataSourceSlave来构造一个HashMap——resolvedDataSources。方便后面根据 key 从该map 中取得对应的dataSource。

我们在看下 AbstractDataSource 接口中的 Connection getConnection() throws SQLException; 是如何实现的:

@Override
  public Connection getConnection() throws SQLException {
    return determineTargetDataSource().getConnection();
  }
ログイン後にコピー

关键在于 determineTargetDataSource(),根据方法名就可以看出,应该此处就决定了使用哪个 dataSource :

protected DataSource determineTargetDataSource() {
  Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
  Object lookupKey = determineCurrentLookupKey();
  DataSource dataSource = this.resolvedDataSources.get(lookupKey);
  if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
    dataSource = this.resolvedDefaultDataSource;
  }
  if (dataSource == null) {
    throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
  }
  return dataSource;
}
ログイン後にコピー

   

 Object lookupKey = determineCurrentLookupKey(); 该方法是我们实现的,在其中获取ThreadLocal中保存的 key 值。获得了key之后,在从afterPropertiesSet()中初始化好了的resolvedDataSources这个map中获得key对应的dataSource。而ThreadLocal中保存的 key 值 是通过AOP的方式在调用service中相关方法之前设置好的。OK,到此搞定!

3. 总结

从本文中我们可以体会到AOP的强大和灵活。

以上就是sping,mybatis 多数据源处理的资料整理,希望能帮助有需要的朋友

更多spring mybatis多数据源实例详解相关文章请关注PHP中文网!

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)