동일한 프로젝트에는 때때로 여러 데이터베이스, 즉 여러 데이터 소스가 포함됩니다. 여러 데이터 소스를 두 가지 상황으로 나눌 수 있습니다.
1) 둘 이상의 데이터베이스는 상관 관계가 없으며 서로 독립적입니다. 실제로 이것은 두 프로젝트로 개발 될 수 있습니다. 예를 들어, 게임 개발에서 하나의 데이터베이스는 플랫폼 데이터베이스이며 플랫폼 아래의 게임에 해당하는 다른 데이터베이스도 사용할 수 있습니다.
2) 두 개 이상의 데이터베이스는 MySQL이 마스터 마스터를 빌드 한 다음 여러 노예가 뒤 따르는 마스터 슬레이브 관계입니다. 또는 MHA로 제작 된 마스터 슬레이브 사본;
현재 Spring Multi-Data 소스를 구축하는 대략 두 가지 방법이 있으며 멀티 데이터 소스의 상황에 따라 선택할 수 있습니다.
1. 스프링 구성 파일을 사용하여 여러 데이터 소스를 직접 구성합니다.
예를 들어, 두 데이터베이스 간의 상관 관계가없는 경우 스프링 구성 파일에서 여러 데이터 소스를 직접 구성한 다음 아래와 같이 트랜잭션 구성을 수행 할 수 있습니다.
<context : component-scan base-package = "net.aazj.service, net.aazj.aop" /> <context : component-scan base-package = "net.aazj.aop" /> <!-속성 파일 소개-> <context : context : property-placeholder 위치 = "classpath : spocig /db.properties"! 이름 = "dataSource"init-method = "init"destroy-method = "close"> <property name = "url"value = "$ {jdbc_url}" /> <property name = "username"value = "$ {jdbc_username}" /> <property name = "password"value "="$ {jdbc_username} " /> <! 이름 = "이니셜 크기"value = "0" /> <!-연결 풀에 사용되는 최대 연결-> <속성 이름 = "maxactive"value = "20" /> <!-사용 가능한 최대 연결 수-> <속성 이름 = "maxidle"value = "20" /> <! <속성 이름 " /> <! 이름 = "maxwait"value = "60000" /> < /bean> <bean id = "sqlsessionfactory"> <property name = "dataSource"ref = "dataSource" /> <property name = "configlocation"value = "classpath : config /mybatis-config.xml" /> <propertlocations ""mapperlocations " value = "classpath*: config/mappers/**/*. xml"/> </bean> <!-단일 JDBC DataSource의 트랜잭션 관리자-> <bean id = "transactionManager"> <property name = "dataSource"ref = "dataSource"/> </bean> <!-annotations를 사용한 transationmanager " /> <bean> <property name = "basepackage"value = "net.aazj.mapper"/> <property name = "sqlsessionfactorybeanname"value = "sqlsessionfactory"/> </bean> <!-@aspectj 스타일의 @aspectj 스타일-> <aop : aspectj-autoproxy/>두 번째 데이터 소스의 구성
<bean name = "dataSource_2"init-method = "init"init "destroy-method ="close "> <property name ="url "value ="$ {jdbc_url_2} " /> <property name ="username "value ="$ {jdbc_username_2} " /> <valess ="jdbc_ < "! 연결 크기 초기화-> <속성 이름 = "이니셜 크기"value = "0" /> <!-연결 풀에 사용되는 최대 연결 수-> <속성 이름 = "maxActive"value = "20" /> <!-최대 유휴 연결 풀-> <속성 이름 = "maxIdle"value = "20" /> <!-<속성 연결-> <속성 연결->! time-> <property name = "maxwait"value = "60000" /> < /bean> <bean id = "sqlsessionfactory_slave"> <property name = "dataSource"ref = "dataSource_2" /> <속성 이름 = "configlocation"value = "classpat : config /mybatis-config-2.xml" /> <property name = "mapperlocations" value = "classpath*: config/mappers2/**/*. xml"/> </bean> <!-단일 jdbc dataSource의 트랜잭션 관리자-> <bean id = "transactionManager_2"> <property name = "dataSource"ref = "dataSource_2"/> </bean> <!-Annotation-<tx : annotation-druce-<tx : <tx : <tx : <tx : <tx-<tx : <tx-<tx. Transaction-Manager = "TransactionManager_2" /> <ean> <속성 이름 = "BasePackage"value = "net.aazj.mapper2" /> <속성 이름 = "sqlsessionfactorybeanname"value = "sqlsessionfactory_2" /> < /bean> " 위에서 볼 수 있듯이 2 개의 데이터 소싱, 2 개의 sqlsessionFactory, 2 개의 TransactionManagers, 그리고 키는 SQLSESSIOTFACTORYBEANNAME 속성을 사용하여 다른 sqlsessionCactory 이름을 주입하는 MappersCannerConfigerer의 구성에 있습니다. 이러한 방식으로, 상응하는 SQLSESSIONFACTORY를 다른 데이터베이스에 해당하는 맵퍼 인터페이스에 주입합니다.
이 여러 데이터베이스의 구성은 분산 트랜잭션을 지원하지 않습니다. 즉, 동일한 트랜잭션에서 여러 데이터베이스를 작동 할 수 없습니다. 이 구성의 장점은 매우 간단하지만 유연하지 않다는 것입니다. 마스터 슬레이브 유형의 다중 데이터 소스 구성에는 그다지 적합하지 않습니다. 마스터 슬레이브 유형의 멀티 데이터 소스 구성은 특히 유연해야하며 비즈니스 유형에 따라 세부 구성이 필요합니다. 예를 들어, 시간이 많이 걸리는 선택 문서를 위해서는 슬레이브에 올려 놓고 업데이트, 삭제 및 기타 작업을 위해 마스터에서만 실행할 수 있습니다. 또한 실시간 요구 사항이 높은 일부 명령문의 경우 마스터에도 마스터에 넣어야 할 수도 있습니다. 예를 들어, 시나리오에서는 무기를 구매하기 위해 쇼핑몰에 가서 구매 작업은 분명히 마스터입니다. 구매가 완료된 후에는 내가 소유 한 무기와 금화를 다시 정체해야합니다. 그런 다음이 쿼리는 마스터에서 실행되는 것을 방지해야하며 슬레이브에 지연이있을 수 있으므로 슬레이브에서 실행할 수 없습니다. 우리는 플레이어가 구매가 성공한 후에 배낭에서 무기를 찾을 수 없다는 것을 알기를 원하지 않습니다.
따라서 마스터 슬레이브 유형 멀티 데이터 소스의 구성을 위해서는 비즈니스에 따라 유연한 구성이 필요하며, 이는 Select를 슬레이브에 배치 할 수 있으며 Select를 슬레이브에 배치 할 수 없습니다. 따라서 위의 데이터 소스 구성은 그다지 적합하지 않습니다.
2. AbstractroutingDatasource 및 AOP를 기반으로 한 멀티 데이터 소스 구성
기본 원칙은 데이터 소스 클래스 ThreadLocalRountingDatasource를 스스로 정의하여 AbstractroutingDatasource를 상속 한 다음 구성 파일에서 마스터 및 슬레이브 데이터 소스를 ThreadLocalRountingDatasource에 주입 한 다음 AOP를 통해 유연하게 구성하는 위치 및 노예 데이터 소스를 선택할 수있는 위치를 정의하는 것입니다. 아래의 코드 구현을 보자.
1) 먼저 다른 데이터 소스를 나타내는 열거를 정의합니다.
패키지 net.aazj.enums; /** * 데이터 카테고리 소스 : 마스터/슬레이브 */public enum dataSources {Master, Slave} 2) 헤드 로컬을 사용하여 각 스레드에 대해 선택할 데이터 소스의 키를 저장하십시오.
패키지 net.aazj.util; import net.aazj.enums.datasources; 공개 클래스 DataSourcetyPemanager {private static final strook <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 정의 및 AbstroutingDatasource를 상속받습니다.
패키지 net.aazj.util; org.springframework.jdbc.datasource.lookup.abstractroutingdatasource; Public Class ThreadLocalRountingDatasource는 AbsTractroutingDatasource를 확장합니다. }}
4) 구성 파일에서 마스터 및 슬레이브 데이터 소스를 ThreadLocalRountingDataSource에 전환합니다.
<context : component-scan base-package = "net.aazj.service, net.aazj.aop" /> <context : component-scan base-package = "net.aazj.aop" /> <!-속성 파일 소개-> <context : context : property-placeholder 위치 = "classpath : spocig /db.properties"! name = "dataSourcemaster"init-method = "init"destroy-method = "close"> <property name = "url"value = "$ {jdbc_url}" /> <속성 이름 = "username"value = "$ {jdbc_username}" /> <property name = "password"value "-jdbc_pass}" /> <! 이름 = "이니셜 크기"value = "0" /> <!-연결 풀에 사용되는 최대 연결-> <속성 이름 = "maxactive"value = "20" /> <!-사용 가능한 최대 연결 수-> <속성 이름 = "maxidle"value = "20" /> <! <속성 이름 " /> <! 이름 = "maxwait"value = "60000" /> < /bean> <!-데이터 소스 소스 슬레이브-> <bean name = "dataSourcesLave"init-method = "init"destroy-method = "close"> <property name = "url"value = "$ {jdbc_url_slave}"/> <username " value = "$ {jdbc_username_slave}" /> <속성 이름 = "password"value = "$ {jdbc_password_slave}" /> <!-초기화 연결 크기-> <속성 크기 "value ="0 " /> <!-속성 풀이 사용되는 최대 숫자 연결-> vally"0 ","20 " />" pool-> <property name = "maxidle"value = "20" /> <!-최소 유휴 연결 풀-> <property name = "minidle"value = "0" /> <!-최대 연결 대기 시간-> <속성 "value ="60000 " /> < /bean> <bean> <bean ="dataSource "> <dataTASOUR" "dataTASOUR" <속성 이름 = "targetDatasources"> <map key-type = "net.aazj.enums.datasources"> <enly key = "mas id = "sqlsessionFactory"> <속성 이름 = "dataSource"ref = "dataSource"/> <property name = "configlocation"value = "classpath : config/mybatis-config.xml"/> <property name = "mapperlocations"value = "classpath*: config/mappers/**/*. jdbc dataSource-> <bean id = "transactionManager"> <속성 이름 = "dataSource"ref = "dataSource" /> < /bean> <!-주석을 사용한 트랜잭션의 정의-> <tx : 주석 중심 트랜잭션 -Manager = "transactionManager" /> <bean> <속성 이름 = "기본 value" 이름 = "sqlsessionFactoryBeanName"value = "sqlsessionFactory"/> -> </bean> 위의 스프링 구성 파일에서는 마스터 데이터베이스 및 슬레이브 데이터베이스의 DataSourcemaster 및 DataSourcesLave를 각각 정의 한 다음 <Bean id = "DataSource">에 주입하여 데이터 소스가 다른 키에 따라 DataSourceMaster 및 DataSourcesLave를 선택할 수 있도록합니다.
5) Spring AOP를 사용하여 DataSource의 키를 지정하므로 DataSource는 키에 따라 DataSourcemaster 및 DataSourcesLave를 선택합니다.
패키지 net.aazj.aop; import net.aazj.enums.datasources; import net.aazj.util.datasourcetypemanager; import org.aspectj.lang.joinpoint; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; import org.aspectj.lang.annotation.pointcut; import org.springframwork.stereotyp.component; @Aspect // aop @component for auto scanpublic class datasourceinterceptor { "@pointcut ("execution (public * net.aazj.service .. *. getUser (..)) ") public void dataSourcesLave () {}; @Before ( "dataSourcesLave ()") public void evere (joinpoint jp) {dataSourcetyPemanager.set (dataSources.slave); } // ...} 여기서 우리는 측면 클래스를 정의합니다. @PointCut ( "execution (public * net.aazj.service .. *. getUser (..)")을 준수하기 전에 DataSourcetyPemanager.set (datasources.slave)를 호출하기 위해 @before를 사용하여 사용합니다. 키 유형은 DataSources로 설정됩니다. 따라서이 방법의 SQL 문은 슬레이브 데이터베이스에서 실행됩니다.
DataSourceinterceptor 측면을 지속적으로 확장하고 특정 서비스 방법에 대한 적절한 데이터 소스에 해당하는 데이터 소스를 지정하기 위해 다양한 정의를 만들 수 있습니다.
이런 식으로 Spring AOP의 강력한 기능을 사용하여 매우 유연하게 구성 할 수 있습니다.
6) AbstractroutingDatasource의 원리 분석
ThreadLocalRountingDatasource는 AbstractroutingDataSource를 상속받으며 추상 방법 보호 추상 객체 DectinEcurrentLookUpkey (); 따라서 다른 데이터 소스에 대한 라우팅 기능을 구현합니다. 원칙을 분석하기 위해 소스 코드부터 시작하겠습니다.
공개 초록 클래스 AbstroutingDatasource 확장 초기화는 초기화 BeanabstractroutingDatasource 구현 초기화를 초기화합니다. 그런 다음 스프링이 Bean을 초기화하면 초기화 비극의 인터페이스를 호출합니다. AbstractroutingDatasource 가이 인터페이스를 어떻게 구현하는지 살펴 보겠습니다. @override public void afterProperTiesset () {if (this.targetDatasources == null) {New New OregalArgumentException ( "속성 'TargetDatasources'가 필요하다"); } this.resolvedDatasources = new Hashmap <개체, DataSource> (this.targetDatasources.size ()); for (map.Entry <object, object> entry : this.targetDatasources.entryset ()) {개체 lookupkey = resolvesPecifiedLookUpkey (entry.getKey ()); DataSource DataSource = resolvesPecifiedDataSource (Entry.GetValue ()); this.resolveddatasources.put (Lookupkey, DataSource); } if (this.defaultTargetDatasource! = null) {this.ResolvedDefaultDatasource = ResolVeseCifiedDatasource (this.defaultTargetDatasource); }} TargetDatasources는 XML 구성 파일에 주입하는 DataSourcemaster 및 DataSourcesLave입니다. AfterPropertiesset 방법이 주입됩니다.
DataSourceMaster 및 DataSourcesLave는 해시 맵 -ResolvedDatasources를 구성합니다. 나중에 키에 따라지도에서 해당 데이터 소스를 얻는 것이 편리합니다.
Connection getConnection ()가 sqlexception을 어떻게 던지는 지 살펴 보겠습니다. AbstractDataSource 인터페이스에서 구현됩니다.
@override public connection getConnection ()는 sqlexception을 던지려고 {return dectioneTargetDatasource (). getConnection (); }핵심은 메소드 이름을 기반으로 볼 수있는 getAggetDatasource ()를 결정하는 것이며 여기에서 사용할 데이터 소스를 결정해야합니다.
보호 된 DataSource degiMinetArgetDatasource () {assert.notnull (this.ResolvedDatasources, "DataSource 라우터가 초기화되지 않았다"); Object Lookupkey = DegiMineCurrentLookUpkey (); DataSource DataSource = this.resolvedDatasources.get (Lookupkey); if (dataSource == null && (this.lenientfallback || lookupkey == null)) {dataSource = this.ResolvedDefaultDatasource; } if (dataSource == null) {throw new new ElegalStateException ( "조회 키를위한 대상 데이터 소스를 결정할 수 없습니다 [" + Xookupkey + "]"; } return dataSource;} Object Lookupkey = DegiMineCurrentLookUpkey (); 이 방법은 우리에 의해 구현되며 ThreadLocal에서 핵심 가치를 저장합니다. 키를 얻은 후, 맵의 키에 해당하는 데이터 소스를 획득 한 후 properTiesset ()에서 초기화 된 resolvedDatasources를 얻으십시오. ThreadLocal에 저장된 주요 값은 AOP를 통해 서비스의 관련 메소드를 호출하기 전에 설정됩니다. 좋아, 여기서 끝났어!
3. 요약
이 기사에서 우리는 AOP의 힘과 유연성을 경험할 수 있습니다.
위는 Mybatis 멀티 데이터 소스 처리의 정보 분류입니다. 도움이 필요한 친구들을 도울 수 있기를 바랍니다