スプリングマルチデータソースの構成(RPM)

:より転載https://www.cnblogs.com/digdeep/p/4512368.html

時にはプロジェクトでそれは、複数のデータソースである、複数のデータベースを必要とします。複数のデータソース2つの場合に分けることができます。

1)2以上のデータベースは、実際には、これを開発するために二つのプロジェクトとして使用することができ、独立して、関係ありません。例えば、ゲーム開発データベース内のデータベース・プラットフォーム、ならびにデータベースに対応する他のゲームプラットフォームでのようです。

2)は、2つの以上のデータベース、スレーブの複数を有するように構築MySQLのマスターマスタがある、主従関係で、構築されたMHAを使用してマスタースレーブまたはコピー;

現時点では私はあなたが複数のデータソースの状況に応じて選択することができ、およそ2つの方法がありますが、春には複数のデータソースを構築するために知っています。

1.直接複数のデータソースを使用して、バネ構成ファイル

たとえば、次のように無相関の場合二つのデータベース、複数のデータソースは、スプリングプロファイルに直接設定することができ、トランザクションを使用して構成されています。

コードをコピー
    <コンテキスト:コンポーネント・スキャンベースパッケージ= "net.aazj.service、net.aazj.aop" /> 
    <コンテキスト:コンポーネント・スキャンベースパッケージ= "net.aazj.aop" /> 
    <! -引入属性文件- > 
    <コンテキスト:プロパティ・プレースホルダーの位置= "クラスパスます。config / db.properties" /> 

    <! -配置数据源- > 
    <Bean名= "データソース"クラス= "com.alibaba.druid.pool .DruidDataSource」INIT-方法= "INIT"破壊法= "クローズ"> 
        <プロパティ名= "URL"値= "$ {jdbc_url}" /> 
        <プロパティ名= "ユーザ名"値= "$ {jdbc_username}" /> 
        <プロパティ名= "パスワード"値= "$ {jdbc_password}" /> 
        <!-初期サイズに参加- > 
        <プロパティ名= "INITIALSIZE"値= "0" /> 
        <! -接続の最大数を使用するには、接続プール- >
        <プロパティ名= "MAXACTIVE"値= "20" /> 
    <! -単一JDBCデータソースのトランザクションマネージャ- >
        <! -连接池最大空闲- > 
        <プロパティ名= "maxIdle"値= "20" /> 
        <! -连接池最小空闲- > 
        <プロパティ名= "minIdle"値= "0" /> 
        <! -获取连接最大等待时间- > 
        <プロパティ名= "MAXWAIT"値= "60000" /> 
    </豆> 
    
    <豆ID = "sqlSessionFactory"クラス= "org.mybatis.spring.SqlSessionFactoryBean"> 
      <プロパティ名= "データソース" REF = "データソース" /> 
      <プロパティ名= "configLocation"値= "クラスパスます。config / MyBatisの-config.xmlの" /> 
      <プロパティ名= "mapperLocations"値= "クラスパス*:設定/マッパー/ ** / *。xmlの」/> 
    </ビーン> 
    
    <ビーンID = "のTransactionManager"クラス= "org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <プロパティ名= "データソース" REF = "データソース" /> 
    </豆> 
    
    <! -使用注釈定义事务- > 
    <TX:アノテーション駆動型トランザクション・マネージャ= "のTransactionManager" /> 
    
    <beanクラス= "org.mybatis.spring.mapper.MapperScannerConfigurer"> 
      <プロパティ名= "basePackage"値= "net.aazj.mapper" /> 
      <プロパティ名= "sqlSessionFactoryBeanName"値= "sqlSessionFactory" /> 
    </豆> 
< -春AOPの@AspectJスタイルの使用を可能にします- >! <AOP:自動プロキシ-AspectJの/> <! - =============== ===============配置された2番目のデータソース- > <Bean名= "dataSource_2"クラス= "com.alibaba.druid.pool.DruidDataSource"のinitメソッド=」INIT」破壊法= "近い"> <プロパティ名= "URL"の値= "$ {jdbc_url_2}" /> <プロパティ名= "ユーザ名"値= "$ {} jdbc_username_2"を/> <プロパティ名= "パスワード"値= "$ {jdbc_password_2}" /> <! -接続の初期化の大きさ- > <プロパティ名= "INITIALSIZE"値= "0" /> <! -最大接続プール接続数を使用して- > <プロパティ名= "MAXACTIVEための"値= /> "20は" <! -接続プールの最大アイドル- > <プロパティ名= "maxIdle"値= "20がある" /> <! -接続最小空きプール- > <プロパティ名= "minIdle "値=" 0 "/> <! -取得最大待ち時間の接続- > <プロパティ名=" MAXWAIT "値=" 60000「/> </豆> <ビーンID = "sqlSessionFactory_slave"クラス= "org.mybatis.spring.SqlSessionFactoryBean">注釈駆動型トランザクション・マネージャ= "transactionManager_2" /> <beanクラス= "org.mybatis.spring.mapper.MapperScannerConfigurer"> <プロパティ名= "データソース" REF = "dataSource_2" /> <プロパティ名= "configLocation"値= "クラスパスます。config / MyBatisの-CONFIG-2.xml" /> <プロパティ名= "mapperLocations"値= "クラスパス*:/ mappers2 / ** / * xmlの設定" /> </豆> <! -単一JDBCデータソースのトランザクションマネージャ- > <豆ID = "transactionManager_2"クラス= "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <プロパティ名= "データソース" REF = "dataSource_2 "/> </豆> < -使用注釈定义事务- >! <TX:注釈駆動型トランザクション・マネージャ=" transactionManager_2" /> <プロパティ名= "basePackage"値= "net.aazj。mapper2" /> <プロパティ名= "sqlSessionFactoryBeanName"値= "sqlSessionFactory_2" /> </豆>
コードをコピー

上記に示したように、データソースは、我々は、2つの2 sqlSessionFactory、のTransactionManager二つ配置されており、そのMapperScannerConfigurer構成の重要な領域- sqlSessionFactoryBeanNameの特性、sqlSessionFactory異なる名前の注入、それは異なるデータベースに対応するようにsqlSessionFactoryに対応するマッパーインターフェース注射。

この構成は同じトランザクションで複数のデータベースを操作することができないで、複数のデータベースの分散トランザクションをサポートしていないことに留意すべきです。この構成の利点は非常に簡単ですが、それは柔軟ではありません。複数のデータソースの点でマスタースレーブ型の構成は、複数のデータソースのマスタ - スレーブ構成を適応していないため、必要性は、特に柔軟性があり、トラフィックの種類に応じて慎重に構成を必要とします。例えば、いくつかの時間がかかり、特に大規模なselect文のために、私たちは奴隷に置く実行すると、更新のため、削除などの操作は、SELECT文の厳しいリアルタイム要件の数に加えて、マスターのみで実行する必要があり、また、我々購入が完了した後、同時に、あなたは私が持っている再照会武器や金に必要な、そのような私は武器を買うためにショッピングモールに行ったシーンとして、購入操作がマスターを与えた後、このクエリのかもしれない - 実行マスターに貼り付ける必要がありますまた、マスターの実装を防ぐために必要があるが、スレーブアップを実行するために、スレーブ上の遅延が発生することがありますので、購入はバックパックの状況は、成功した後ではない、我々は、プレイヤーが発見する必要はありませんが、武器が現れ見つけることができません。

そうサービスによれば、複数のデータソースのマスタスレーブ型のために設定が選択スレーブ上に配置することができない選択スレーブ、上に配置することができる柔軟な構成とすることが必要です。データソースが少ない上記に適応されるように設定。

構成およびAOP AbstractRoutingDataSource複数のデータソースに基づいて、2

基本原理は、柔軟な構成にAOPによって、構成ファイル内のThreadLocalRountingDataSourceにマスタとスレーブのデータソースに注入し、そして、AbstractRoutingDataSourceを継承するために、我々はDataSourceクラスのThreadLocalRountingDataSourceの定義を所有していることで、内の領域を必要とするものの分野におけるマスターデータソースを選択しますスレーブデータソースを選択します。達成するためのコードで次を見:

1)異なるデータソースを表すために列挙型を定義します。

コードをコピー
パッケージnet.aazj.enums; 

/ ** 
 *カテゴリデータソース:マスタ/スレーブ
 * / 
公共enumのデータソース
    MASTER、SLAVE 
}
コードをコピー

2)選択するTheadLocalフラグデータソース(キー)を介して各スレッドを保存します。

コードをコピー
パッケージnet.aazj.util。

輸入net.aazj.enums.DataSources。

パブリッククラスDataSourceTypeManager { 
    プライベート静的最終ThreadLocalの<データソース> dataSourceTypes =新しいThreadLocalの<データソース>(){ 
        @Overrideは、
        データソースはinitialValue(保護){ 
            DataSources.MASTERを返します。
        } 
    }。
    
    公共の静的なデータソースを取得する(){ 
        )(dataSourceTypes.getを返します。
    } 
    
    パブリック静的ボイド集合(データソースのdataSourceType){ 
        dataSourceTypes.set(dataSourceType)。
    } 
    
    パブリック静的ボイドリセット(){ 
        dataSourceTypes.set(DataSources.MASTER0)。
    } 
}
コードをコピー

3) ThreadLocalRountingDataSourceの定義、継承AbstractRoutingDataSource:

コードをコピー
パッケージnet.aazj.util。

輸入org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource。

パブリッククラスThreadLocalRountingDataSourceはAbstractRoutingDataSource {延び
    @Override 
    保護オブジェクトdetermineCurrentLookupKeyを(){ 
        (DataSourceTypeManager.getを返します)。
    } 
}
コードをコピー

4) ThreadLocalRountingDataSourceに構成ファイル内注射マスタとスレーブのデータソース:

コードをコピー
    <コンテキスト:コンポーネント・スキャンベースパッケージ= "net.aazj.service、net.aazj.aop" /> 
    <コンテキスト:コンポーネント・スキャンベースパッケージ= "net.aazj.aop" /> 
    <! -引入属性文件- > 
    <コンテキスト:プロパティ・プレースホルダーの位置= "クラスパスます。config / db.properties" />     
    <! -配置数据源マスター- > 
    <Bean名= "dataSourceMaster"クラス= "com.alibaba.druid。 pool.DruidDataSource "のinitメソッド= "近いINIT"=破壊法""> 
        <プロパティ名= "URL"の値= "$ {jdbc_url}"/> 
        <プロパティ名= "ユーザ名"値=" $ {jdbc_username} "/> 
        <プロパティ名="パスワード」値= "$ {jdbc_password}" />
        <! -サイズ初期接続- > 
        <プロパティ名= "INITIALSIZE"値= "0" /> 
        <! -最大接続数、接続プール- >
        <プロパティ名= "MAXACTIVE"値= "20" />  
        <! -最大アイドル接続プール- >
        <プロパティ名= "maxIdle"値= "20" /> 
        <! -连接池最小空闲- > 
        <プロパティ名= "minIdle"値= "0 "/> 
        <! -获取连接最大等待时间- > 
        <プロパティ名=" MAXWAIT "値= "60000"/> 
    </豆>     
    <! -配置数据源スレーブ- > 
    <Bean名=" dataSourceSlave "クラス=" com.alibaba.druid.pool.DruidDataSource」INIT-方法= "INIT"破壊法= "クローズ"> 
        <プロパティ名= "URL"値= "$ {jdbc_url_slave}" /> 
        <プロパティ名= 「ユーザ名」の値=」$ {jdbc_username_slave}」/> 
        <プロパティ名= "パスワード"値= "$ {jdbc_password_slave}" /> 
        <! -初始化连接大小- > 
        <プロパティ名= "INITIALSIZE"値= "0" />=値"$ {jdbc_password_slave}" /> 
        <! - > -接続プールは、接続の最大数を使用するには
        、<プロパティ名= "MAXACTIVEための"値= "20がある" /> 
        <! -接続プールの最大自由を- >
        <プロパティ名= "maxIdle"値= "20は" /> 
        <! -接続プール最小アイドル- > 
        <プロパティ名= "minIdle"値= "0" /> 
        <! -最大待ち時間接続を取得します- > 
        <プロパティ名= "MAXWAIT"値= "60000" /> 
    </ビーン>     
    <ビーンID = "データソース"クラス= "net.aazj.util.ThreadLocalRountingDataSource"> 
        <プロパティ名= "defaultTargetDataSource" REF = "dataSourceMaster" /> 
        <プロパティ名= "targetDataSources"> 
            <> = "net.aazj.enums.DataSources"キータイプの地図 
    <ビーンID = "sqlSessionFactory"クラス= "org.mybatis.spring.SqlSessionFactoryBean">enums.DataSources "> 
                <入力キー="マスター」値-REF = "dataSourceMaster" /> 
                <エントリキー= "スレーブ"値-REF = "dataSourceSlave" />
                <! -这里还可以加多个のdataSource - > 
            </ MAP> 
        </ property>の
    </豆>     
      <プロパティ名= "データソース" REF = "データソース" /> 
      <プロパティ名= "configLocation"値=」クラスパスます。config / MyBatisの-config.xmlの」/> 
      <プロパティ名= "mapperLocations"値= "クラスパス*:設定/マッパー/ ** / * xmlの" /> 
    </豆>     
    <! -のトランザクションマネージャ単一JDBCデータソース- > 
    <豆ID = "のTransactionManager"クラス= "org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <プロパティ名= "データソース" REF = "データソース" />
    </豆>     
    <! -注釈を使用してトランザクションを定義- > 
    <TX:注釈駆動型トランザクション・マネージャ= "のTransactionManager" /> 
    <beanクラス= "org.mybatis.spring.mapper。MapperScannerConfigurer "> 
      <プロパティ名=" basePackage」値= "net.aazj.mapper" />
      <! - <プロパティ名= "sqlSessionFactoryBeanName"値= "sqlSessionFactory" /> - > 
    </豆>        
コードをコピー

上記スプリングプロファイルは、我々は、マスターデータベースとスレーブデータベースに焦点を当てたデータソースを定義し、dataSourceSlave 2 dataSourceMasterされ、その後<ビーンID =「データソース」クラス=「net.aazj.util.ThreadLocalRountingDataSource」>に注ぎ、私たちデータソースは、異なる鍵とdataSourceSlave dataSourceMasterに応じて選択することができるからです。

5)使用データソーススプリングAOPは、キーがdataSourceMasterとデータソースによるdataSourceSlaveを選択することにより、キーを指定します。

コードをコピー
パッケージnet.aazj.aop。

輸入net.aazj.enums.DataSources。
輸入net.aazj.util.DataSourceTypeManager。

輸入org.aspectj.lang.JoinPoint。
輸入org.aspectj.lang.annotation.Aspect; 
輸入org.aspectj.lang.annotation.Before; 
輸入org.aspectj.lang.annotation.Pointcut; 
輸入org.springframework.stereotype.Component。

@Aspect //のためのAOP 
@Component //自動スキャン用
(0)// @Transactional前に実行@Order パブリッククラスDataSourceInterceptor { @Pointcut(「実行(公開* net.aazj.service .. *。のgetUser(..) )」) 公共ボイドdataSourceSlave(){}。 @Before( "dataSourceSlave()") の前に公共のボイド(ジョインポイントのJP){ DataSourceTypeManager.set(DataSources.SLAVE)。 }
// ... ... }
コードをコピー

ここでは、クラスを定義する側面は、我々は@Pointcutに沿って@Beforeする方法を使用します(「実行(パブリック*のnet.aazj.service .. *。GETUSER(..))」) の呼び出しの前に、コールDataSourceTypeManager。セット(DataSources.SLAVE)は、キータイプDataSources.SLAVEのために提供され、我々は、データソースのdataSourceキー= DataSources.SLAVE dataSourceSlaveを選択します。したがって、SQLステートメントのための方法は、サインを介してスレーブデータベース(上で実行される劉1987アラート、態様のうち実行順序の問題はここに存在して、データソースアスペクトハンドオーバアスペクト@Transactional前に実行されなければならないことを保証しなければなりませんそれは)@Order(0)データ・ソースが@Transactional前に実行切り替えられることを確実にするためにここで使用されます。

我々は、DataSourceInterceptorにこの局面を展開における様々な定義を行い、サービスの特定の方法のためのデータソースを、対応する適切なデータソースを指定するために続けることができます。

私たちは春AOPの電源を使用できるように、非常に柔軟に構成します。

6)AbstractRoutingDataSource原則分析

ThreadLocalRountingDataSourceは、その保護された抽象オブジェクトdetermineCurrentLookupKey()を達成するためAbstractRoutingDataSource、抽象メソッドを継承し、異なるデータソースを達成するために、機能ルーティング。我々は、下の原則ソースコード解析から起動します。

パブリック抽象クラスAbstractRoutingDataSourceはAbstractDataSourceがInitializingBeanを実装して拡張します
AbstractRoutingDataSourceがInitializingBeanは、その後、それはInitializingBeanインタフェース呼ぶ豆の初期化時に春の実現
)、ボイドafterPropertiesSetは(例外がスローされます。私たちはAbstractRoutingDataSourceは、このインタフェースの実装でどのように見えますか:
コードをコピー
    @Override 
    ます。public void afterPropertiesSet(){ 
        (this.targetDataSources == nullでは){場合
            新しいIllegalArgumentExceptionをスローし( "プロパティ'targetDataSources'が必要です"); 
        } 
        this.resolvedDataSources =新規のHashMap <オブジェクト、データソース>(this.targetDataSources.size())。
        用(のMap.Entry <オブジェクト、オブジェクト>エントリ:this.targetDataSources.entrySet()){ 
            オブジェクトlookupKey = resolveSpecifiedLookupKey(entry.getKey())。
            データソースデータソース= resolveSpecifiedDataSource(entry.getValue())。
            this.resolvedDataSources.put(lookupKey、データソース)。
        } 
        IF(this.defaultTargetDataSource!= NULL){
            this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource)。
        } 
    }
コードをコピー
我々は、XML設定ファイルdataSourceMasterとdataSourceSlaveに注入targetDataSources。afterPropertiesSet方法は、注入使用することです
HashMapを構築するためにdataSourceMasterとdataSourceSlaveを- resolvedDataSources。後でデータソースに基づいてマップから対応するキーを取得促進します
私たちは、接続のgetConnection AbstractDataSourceインターフェイス()のSQLExceptionがスローに見える;実現する方法を:
    @Override 
    パブリック接続のgetConnection()のSQLException {スロー
        (determineTargetDataSourceを返す)のgetConnectionを()。
    }

キーは、使用がここで決定されるべきである見ることができるメソッド名に応じて、determineTargetDataSource()であるデータソース:

コードをコピー
    保護されたデータソースdetermineTargetDataSource(){ 
        Assert.notNull(this.resolvedDataSources、 "初期化されないデータソースルータ")。
        オブジェクトlookupKey = determineCurrentLookupKey()。
        データソースデータソース= this.resolvedDataSources.get(lookupKey)。
        IF(データソース== NULL &&(this.lenientFallback || lookupKey == NULL)){ 
            データソース= this.resolvedDefaultDataSource。
        } 
        場合(データソース== NULL){ 
            新しいIllegalStateExceptionがスロー( "ルックアップキーのターゲットデータソースを決定することはできません[" + lookupKey + "]"); 
        } 
        DataSourceを返します。
    }
コードをコピー
オブジェクトlookupKey = determineCurrentLookupKey();これは、取得したキー値のThreadLocalに格納されている手法実装です。キーを取得した後、
resolvedDataSourcesのafterPropertiesSet良いからこのマップをキーに対応するデータソースの初期化()を取得します。キーの値に保存されているThreadLocalのは
良いのサービスを呼び出す前に、AOP関連の方法により提供されます。OK、これを取得!

7)拡張ThreadLocalRountingDataSource

私たちの上にだけ選択し、マスタ・スレーブ・データ・ソースを実現。あなたは、複数のマスタ、または複数のスレーブを持っている場合。HAは、リンクされたマスターの一つは自動的に別の親局に切り替えているときに達成されるべきマスタの複数からなる、この関数はLVS / keepalivedのは、達成さらに膨張ThreadLocalRountingDataSourceによって実現することができる、スレッドは、さらに缶を使用することができますそれぞれは、通常、MySQLが実現するかどうかをテストするために1秒に特化。一方、子局ハングアップ実装ロードバランシングから削除され、複数の中から負荷分散を達成するために、スレーブにもが、この機能のいずれかを使用することができるLVS / keepalivedのも一歩前進を取ることによって拡張することが、達成しました達成するThreadLocalRountingDataSource。

3.まとめ

この記事から、我々はAOPは強力で柔軟な理解することができます。

本明細書で使用される場合、それは、実際に、Hibernateはまた、同様の構成でなければならない使用、MyBatisのです。

おすすめ

転載: www.cnblogs.com/xuningchuanblogs/p/10950436.html