기능적 요구 사항은 회사가 대규모 운영 플랫폼을 구축해야한다는 것입니다.
1. 운영 플랫폼에는 사용자, 역할, 메뉴, 부품 및 권한과 같은 기본 기능을 유지하는 자체 데이터베이스가 있습니다.
2. 운영 플랫폼은 또한 다른 다른 서비스 (서비스 A, 서비스 B)의 백엔드 운영을 제공해야하며 서비스 A 및 서비스 B의 데이터베이스는 독립적입니다.
따라서 작동 플랫폼은 작동 라이브러리, 라이브러리 및 B 라이브러리의 최소 3 개의 라이브러리를 연결해야하며 각 기능 요청에 대한 해당 데이터 소스로 자동 전환하기를 희망합니다 (최종 구현은 서비스 수준으로 전환하고 각 DAO 계층의 메소드로 전환하는 것입니다. 우리 시스템의 기능은 상대적으로 서로 독립적입니다).
1 단계 : 여러 데이터 소스를 구성합니다
1. 데이터 소스 정의 :
내가 사용하는 데이터 소스는 Alibaba의 Druiddatasource입니다 (DBCP에서는 괜찮습니다). 구성은 다음과 같습니다.
<!-op dataSource-> <bean id = "opdatasource"init-method = "init"init "destroy-method ="close "> <속성 이름 ="url "value ="$ {db.master.url} " /> <속성 이름 ="username "value ="$ {db.mas <property name = "driver className"value = "$ {db.mas 이름 = "testonborring"value = "false" /> <속성 이름 = "testonReturn"value = "false" /> <속성 이름 = "testwhileIdle"value = "true" /<property name = "timebetweenevictionRunsmillis"value = "600000" /> <property name = "property name ="300000 ""300000 ". value = "true" /> <property name = "removeabandonedTimeout"value = "1800" /> <속성 이름 = "logabandoned"value = "true" /> <!-통계를 모니터링하기위한 필터 구성-> <속성 이름 = "filters"value = "config, mergestat, 벽, log4j2" />> value = "connectionpropperts" "connectionproppts" < /bean> <!-servera dataSource-> <bean id = "serverAdataSource"init-method = "init"init "destroy-method ="close "> <property name ="url "value ="$ {db.servera.mas value = "$ {db.servera.mas value = "60000" /> <속성 이름 = "validationQuery"value = "select 'x'" /> <property name = "readonborring"value = "false" /<property name = "testonreturn"value = "false" /> <property name = "testonReturn"value = "false" /> <property name = "testOnreturn ="value = "valud" "valud" 이름 = "TimeBetweenevictionRunsmillis"value = "600000" /> <속성 이름 = "MineVictableDletimEmillis"value = "300000" /> <property name = "removeaBandOned"value = "true" /> <속성 이름 = "removeAbandonEdTimeout"value = "1800" /> <속성 이름 = "logab end"= "true" ", 구성. 모니터링 통계 인터셉트-> <속성 이름 = "필터"value = "config, mergestat, 벽, log4j2" /> <속성 이름 = "ConnectionProperties"value = "config.decrypt = true" /> < /bean> <!-serverb datasource-> <bean id = "serverbdatasource"init-method = "init <ur same" "" "" "" "" "" "" "" "" "" "" " value = "$ {db.serverb.mas />> <속성 이름 = "이니셜 크기"값 = "5" /> <속성 이름 = "maxActive"value = "100" /> <속성 이름 = "minidle"value = "10" /> <property name = "maxwait"value = "60000" /> <속성 이름 = "valicationQuery"value = "select 'x' /> <property name ="rethonbronce ""testOnet " value = "false" /> <property name = "testwhileIdle"value = "true" /> <property name = "timebetweenevictionRunsmillis"value = "600000" /> <property name = "minevictableDletimemillis"value = "300000" /> <property name = "removeabaandoned"value = "value =" "value" <속성 이름 = "logabandoned"value = "true" /> <!-통계 모니터링을위한 필터 구성-> <속성 이름 = "필터"value = "config, mergestat, 벽, log4j2" /> <속성 이름 = "ConnectionProperties"value = "config.decrypt = true"< /bean>Opdatasource (운영 플랫폼 자체의 데이터 소스), ServerAdatasource 및 ServerBdatasource의 세 가지 데이터 소스를 구성했습니다.
2. Multipledatasource 구성
Multipledatasource는 위의 세 데이터 소스에 대해 하나의 프록시와 같습니다. Spring/Mybatis와 진정으로 결합되면 Multipledatasource 및 별도로 구성된 데이터 소스 사용은 다르지 않습니다.
<!-스프링 통합 mybatis : multipledatasource 구성-> <bean id = "sqlsessionfactory"> <속성 이름 = "dataSource"ref = "multipledatasource"/> <!-자동 스캔 mapping.xml 파일-> <property name = "mapperlocations "> valuepath*:/sqlmporx xmll/*. <value> classpath*:/sqlmapperxml/*/*. xml </value> </list> </property> <속성 이름 = "configlocation"value = "classpath : xml/mybatis-config.xml"> </property> <property name = "inftealiasespackage"value = "com.xxx.platform.model"/>>/>. ref = "globalConfig"/> <property name = "플러그인"> <array> <!-페이지 분해 플러그인 구성-> <bean id = "PageInation Interceptor"> <property name = "decienttype"value = "mysql"/> <속성 이름 = "옵티izetype"value = "alidruid"/> </array </bean> </bean> </bean>> </bean>> </bean>> -> <bean id = "MapperscannerConfigurer"> <!-DAO 인터페이스 동적 구현의 경우 인터페이스가 어디에 있는지 알아야합니다.> <속성 이름 = "basePackage"value = "com.xxx.platform.mapper"/> <속성 이름 = "sqlsessionCactoryBeanname"value "> </bean> </bean>> </bean>> <bean id = "globalConfig"> <속성 이름 = "idtype"value = "0"/> <속성 이름 = "dbcolumnunderline"value = "true"/> </bean> <!-트랜잭션 관리 multipledatasource-> <bean id = "transactionManager"> <property name = "dataSource"ref = "ref =/bean> </property">
Multipledatasource의 위치를 이해 한 후 Multipledatasource를 구현하는 방법에 중점을 두겠습니다. 구성 파일은 다음과 같습니다.
<bean id = "multipledatasource"> <속성 이름 = "defaultTargetDatasource"ref = "opdatasource" /<속성 이름 = "TargetDatasources"> <map> <enterd key = "opdataSource"value-ref = "OpdatAsource" /> <enterd key = "serverAdatasource"enter-ref = "serverAdasource" /< key = "serverbdatasource"value-Ref = "ServerBdatasource"/> </map> </property> </bean>
구현 된 Java 코드는 다음과 같습니다. 너무 많은 설명이 필요하지 않으며 한눈에 매우 분명합니다.
import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource;/** * * @classname : multipledatasource * @description : <br> * @author : yuzhu.peng * @date : 2018 년 1 월 12 일, 2018 년 1 월 12 일. ABSTRACTROUTINGDATASOURCE {Private STATIC Final ThreadLocal <string> DataSourcekey = New InheritableThreadLocal <string> (); public static void setDatasourcekey (String DataSource) {dataSourcey.set (DataSource); } @Override Protected Object DeciNeCurrentLookUpkey () {return DataSourceKey.get (); } public static void removedatasourcekey () {dataSourcey.remove (); }}Spring의 AbstractroutingDatasource에서 상속 된 것은 추상적 인 방법 DecileCurrentLookupkey를 구현합니다. 이 방법은 데이터베이스 연결을 얻을 때 마다이 연결의 데이터 소스 데이터 소스를 결정합니다. 스프링 코드가 명확한 것을 볼 수 있습니다.
/*연결 GET CONNECTION*/ public Connection getConnection () 던지기 SQLEXCEPTion {return dectinEtArgetDatAsource (). getConnection (); } 보호 된 DataSource dectainetArgetDatasource () {assert.notnull (this.ResolvedDatasources, "DataSource 라우터가 초기화되지 않음"); /*여기서 결정적인 데이터 소스 이름을 얻는 초록 인터페이스입니다. DataSource 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; } /*Abstract Interface : 즉, Multipledatasource* / Protected Abstract Object DecineCurrentLookupkey ()에 의해 구현 된 인터페이스;2 단계 : 데이터 소스를 동적으로 전환하여 모든 요청 (서비스 방법 수준)
구현 아이디어는 Spring의 AOP 아이디어를 사용하여 각 서비스 메소드 호출을 가로 채고 메소드의 전체 경로 이름에 따라 MultipledAtasource에서 데이터 키를 동적으로 전환하는 것입니다. 우리의 프로젝트는 다른 서비스의 운영, 즉 다른 데이터베이스의 운영을 위해 서로 독립적입니다. 동일한 서비스 방법에서 다른 데이터 소스를 호출하는 것이 좋습니다. 이런 식으로, 우리는 스위칭 주파수를 DAO 레벨, 즉 SQL 레벨에 배치 해야하는지 동적으로 결정해야합니다. 또한 거래 관리가 편리하지 않습니다.
동적 스위칭 데이터 소스의 AOP 구현을 살펴 보겠습니다.
import java.lang.reflect.proxy; import org.apache.commons.lang.classutils; import org.aspectj.lang.joinpoint; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.be -beore; import org.springframework.core.core. * @author yuzhu.peng * @since 2018-01-15 * / @ater @order @order (1) public class multipledatasourceinterceptor { /** * 인터셉터는 모든 비즈니스 구현 클래스를 요청하기 전에 데이터 소스를 전환하는 데 특별한주의를 기울입니다. 여러 데이터 소스가 사용되므로 *serviceimpl에서만 Mapper를 호출하는 것이 가장 좋습니다. 그렇지 않으면, 기본 데이터 소스가 아닌 테이블을 호출 할 때, 테이블에 존재하지 않는 예외는보고됩니다** @param joinpoint* @throws trashable*/ @before ( "execution (* com.xxxx.platform.service ..*. joinpoint.getTarget (). getClass (); 문자열 className = clazz.getName (); if (classUtils.isAssignable (Clazz, proxy.class)) {className = joinpoint.getSignature (). getDeclaringTypename (); } // 클래스 이름으로 Servera 데이터 소스를 설정하십시오. 그렇지 않으면 기본값은 백그라운드의 데이터 소스입니다 if (className.contains ( ". servera.")) {multipledatasource.setDatasourcey (dbconstant.data_source_servera); } else if (className.contains ( ". serverb.")) {multipledatasource.setdatasourcey (dbconstant.data_source_serverb); } else {multipledatasource.setDatasourcekey (dbconstant.data_source_op); }} /*** 작업이 완료되면 현재 데이터 소스가 릴리스되면 릴리스되지 않으면 자주 클릭하면 데이터 소스 충돌이 발생합니다. 다른 데이터 소스의 테이블이지만 다른 데이터 소스로 실행됩니다. 이 보고서는 존재하지 않습니다** @param jointoint* @Throws Throwable*/ @after ( "execution (* com.xxxx.service ..*.*. }}모든 ServiceMPL 메소드를 가로 채고, 데이터 소스 함수가 메소드의 정규화 된 이름에 속하는 데이터 소스 기능을 판단한 다음 해당 데이터 소스를 선택하십시오. 분포가 완료된 후 현재 데이터 소스를 해제하십시오. Spring의 @Order, Annotation을 사용했으며 여러 AOP를 정의 할 때 다음에 대해 이야기 할 것입니다. 주문은 매우 유용합니다.
다른:
처음에는 프로젝트가 거래를 도입하지 않았으므로 모든 것이 괜찮 았습니다. 매번 올바른 데이터 소스에 액세스 할 수 있습니다. Spring의 트랜잭션 관리에 참여한 후에는 데이터 소스를 동적으로 전환 할 수 없습니다 (트랜잭션이 효과적이지는 않지만 동시에 유효하지 않은 것 같습니다). 나중에 그 이유는 AOP의 실행 순서 였으므로 위에서 언급 한 스프링 순서를 사용했습니다.
순서가 작을수록 실행이 첫 번째입니다. 이 시점에서 데이터 소스를 동적으로 전환 할뿐만 아니라 동일한 데이터 소스에서 트랜잭션을 성공적으로 사용할 수 있습니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.