Os requisitos funcionais são que a empresa precisa construir uma grande plataforma operacional:
1. A plataforma de operação possui seu próprio banco de dados, mantendo funções básicas, como usuários, funções, menus, peças e permissões.
2. A plataforma de operação também precisa fornecer operações de back-end de outros serviços diferentes (Serviço A, Serviço B) e os bancos de dados do Serviço A e do Serviço B são independentes.
Portanto, a plataforma de operação deve conectar pelo menos três bibliotecas: biblioteca de operação, uma biblioteca e biblioteca B e esperar alternar automaticamente para a fonte de dados correspondente para cada solicitação de função (minha implementação final é mudar para o nível do método do serviço e mudar para o método de cada camada DAO. As funções do nosso sistema são relativamente independentes).
Etapa 1: Configurar várias fontes de dados
1. Defina a fonte de dados:
A fonte de dados que eu uso é o druiddataSource do Alibaba (está bem com o DBCP, isso é o que for). A configuração é a seguinte:
<!-op DataSource-> <bean id = "opdataSource" init-method = "init" destrow-method = "close"> <propriedade name = "url" value = "$ {db.master.url}" /> <names name = "username" = "$ {db.master.user}" value = "$ {db.master.password}" /> <propriedade name = "driverclassName" value = "$ {db.master.driver}" /> <names name = "inicialsize" value = "5" /> <names da propriedade ">" maxactive "" "100" /> <names = "minidle" "10" ""> ">" ">" /namesize " /" minuex " /> <name" ">"> "" " /" /name " /" minidle " /" ""> ">" 3) /"3) /namesize" /"5" /> <names da propriedade "> name = "validationQuery" value = "selecione 'x'" /> <propriedade name = "testOnBorrow" value = "false" /> <names name = "testOnReturn" value = "false" /> <names name = "testhileidle" value = "true" /> <nome da propriedade = "theBeenEvictionRunsmillis" valueLel = "600000" /> <nome da propriedade = "theBeenEvictionRunSmillis" valor = "600000" /> <> value = "300000" /> <Nome da propriedade = "Removeabandon" value = "true" /> <names name = "removeabandonTimeout" value = "1800" /> <names name = "logabandono" value = "true" /> <!-Configure filtros para monitoramento de estatísticas-> <nome da propriedade ("Filters" "<!-Config name = "ConnectionProperties" value = "config.Decrypt = true" /> </i bean> <!-servera DataSource-> <bean id = "serveradataSource" init-method = "init" de destro-method = "close"> <nome da propriedade = "urbune" = "$ {db.serverra.Ma.Master.ur"> <nome da propriedade ") value="${db.serverA.master.user}" /> <property name="password" value="${db.serverA.master.password}" /> <property name="driverClassName" value="${db.serverA.master.driver}" /> <property name="initialSize" value="5" /> <property name="maxActive" value = "100" /> <propriedade name = "minidle" value = "10" /> <name da propriedade = "maxwait" value = "60000" /> <propriedade name = "validationQuery" value = "selecione 'x'" /> <names name = "testonborrow" /"false" /> <nome do nome = "TestOnTurturn" = "false" "" " /") name = "testOnReturn" value = "false" /> <propriedade name = "testhileIdle" value = "true" /> <names name = "timebetweenEvictionRunsmillis" value = "600000" /> Property Name = "mineVeaDoned" "Value" = "300000" /> name = "RemovaBandonen" value = "1800" /> <propriedade name = "logabandoned" value = "true" /> <!-configure filtros para monitoramento estatísticas intercepta-> <propriedade name = "filters" value = "config, mergestat, parede, log4j2" /> <nome da propriedade = "conexão de conexão" = "config.decrypt = true" /> <names da propriedade = "conexão"> id = "serverbdataSource" init-method = "init" destruir-method = "close"> <propriedade name = "url" value = "$ {db.serverb.master.url}" /> <nome da propriedade = "nome de usuário" value = "$ {db.serverb.master.user}" /> Valor = "$ {db.serverb.master.password}" /> <propriedade name = "driverclassName" value = "$ {db.serverb.master.driver}" /> <names name = "initialsize" value = "5" /> <name = "maxativo" " /" "100" /> <> <TeRa) value = "60000" /> <propriedade name = "validationQuery" value = "selecione 'x'" /> <propriedade name = "testOnBorrow" value = "false" /> <names name = "testOnReturn" value = "false" /<weeevicttictynisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnisnissmilnis "Value" /> <nome da propriedade " /" <> name = "minevictableIdletimemillis" value = "300000" /> <names name = "removeabandon" value = "true" /> <propriedade name = "removeabandonTimeout" value = "1800" /> <propriedades name = "logabandoned" value "" /> <!-Configurar filtros para estatísticas de monitoramento- value = "config, mergestat, parede, log4j2" /> <propriedade name = "ConnectionProperties" value = "config.decrypt = true" /> </siean>Configurei três fontes de dados: opdatasource (a fonte de dados da própria plataforma operacional), ServerAdatasource e ServerBDatasource.
2. Configure multiplimedatasource
A multiplicação de ou seja equivalente a um proxy para as três fontes de dados acima. Quando é verdadeiramente combinado com a primavera/mybatis, o uso de dados de dados multiplicados e configurados separadamente não são diferentes:
<!-Integração da mola mybatis: configure multiplledatasource-> <bean id = "sqlSessionFactory"> <propriedade name = "DataSource" ref = "multipledatasource"/> <!-Scan Mapping.xml File-> <nome da propriedade = "mapperLocations"> <list> <talty> <Value> ClassPath*:/sqlmapperxml/*/*. xml </value> </list> </property> <propriedade name = "configLocation" value = "classPath: xml/mybatis-config.xxxxx"> </propriedade> <propriedades (typeAliasesPackage "value =" com.xxxxxx.xxx.xx.plimt = "nameslespackage" talue = "com.xxxxxx.xxx.xx.xl"> </propriedade> <nome do nome "typeAliasEngAge") ref = "GlobalConfig"/> <propriedade name = "plugins"> <Array> <!-Configuração de plug-in de paginação-> <bean id = "paginação -Verceptor"> <propriedade name = "dialectType" value = "mysql"/> <nome da propriedade = "otimizeType" = "Alidruid/</> </> </> implementação-> <bean id = "mapperscannerconfigurer"> <!-Para a implementação dinâmica da interface Dao, você precisa saber onde está a interface-> <propriedades name = "Basepackage" value = "com.xxx.platform.mapper"/> <nome da propriedade "sqlSessionBeanName <Talue =" sption) <) <! Configuração-> <bean id = "globalConfig"> <propriedade name = "idtype" value = "0"/> <names name = "dbcolumnunderline" value = "true"/> </bean> <!-gerenciamento de transação de datas multiplledatasource-> <bean = "transaction>"> <wened name = "
Depois de entender a localização do multipliplasource, vamos nos concentrar em como implementar o multiplleataSource. O arquivo de configuração é o seguinte:
<bean id = "multiplledatasource"> <propriedade name = "defaultTargetDataSource" ref = "opdataSource" /> <propriedade name = "TargetDataSources"> <pap> <key de entrada (keyAtAtAdatAtsOd " key = "serverbdatasource" value-ref = "serverbdataSource"/> </map> </property> </bean>
O código Java implementado é o seguinte, e não há necessidade de muita explicação, e é muito claro de relance:
importar org.springframework.jdbc.dataSource.lookup.abstractroutingDataSource;/** * * @className: multiplledatasource * @Description: configure várias fontes de dados <br> * @author: yuzhu.peng * @date: 12 de janeiro de 12, 2018 em 40. AbstraCtroutingDataSource {private Static Final ThreadLocal <String> DataSourceKey = new HeritableThreadLocal <String> (); public static void setDataSourceKey (String DataSource) {DataSourceKey.Set (DataSource); } @Override Objeto protegido DeclarPReCurrentLookupKey () {return dataSourceKey.get (); } public static void removeTataSourceKey () {DataSourceKey.remove (); }}Herdado do abstractroutingDataSource da primavera, implementa o método abstrato DeterminCurrentLookupKey. Este método determinará a fonte de dados da fonte de dados para esta conexão sempre que a conexão do banco de dados for obtida. Você pode ver o código da primavera para ficar claro:
/*Get Connection*/ Public Connection getConnection () lança SQLEXCECCIONE {return determinetargetDataSource (). GetConnection (); } DataSource protegido DeconmarETargetDataSource () {Assert.NotNull (this.ResolvedDatasources, "DataSource Router não inicializado"); /*O DeclareNecurrentLookUpKey aqui está uma interface abstrata, obtendo o nome da fonte de dados específico*/ objeto lookupKey = determinamEcurrentLookupKey (); DataSource DataSource = (DataSource) this.resolvedDataSources.get (LookUpKey); if ((DataSource == NULL) && (((this.lenientFallback) || (LookUpKey == NULL)))) {DataSource = this.ResolvedDefaultDataSource; } if (DataSource == NULL) {TOME NOVA ILGLELGALSTATEEXCECTION ("Não é possível determinar o DataSource de destino para a chave de pesquisa [" + LookUpKey + "]"); } retornar DataSource; } /*Interface abstrata: isto é, a interface implementada por nossa multiplipledataSource* / abstrato protegido DeterminCurrentLookKey ();Etapa 2: Alterne dinamicamente a fonte de dados todas as solicitações (nível do método de serviço)
A idéia de implementação é usar a ideia AOP da Spring para interceptar cada chamada do método de serviço e alternar dinamicamente a chave dos dados no multiplledatasource de acordo com o nome geral do caminho do método. Nosso projeto, para as operações de diferentes serviços, ou seja, diferentes bancos de dados, é independente um do outro. Não é recomendável chamar diferentes fontes de dados no mesmo método de serviço. Dessa forma, precisamos determinar dinamicamente se a frequência de comutação precisa ser colocada no nível DAO, ou seja, o nível SQL. Além disso, o gerenciamento de transações não é conveniente.
Vejamos a implementação da AOP de fontes de dados dinâmicas de comutação:
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.Before;import org.springframework.core.annotation.Order;/** * Data source switching AOP * * @author yuzhu.peng * @since 2018-01-15 * / @aspecto @order (1) classe pública multiplipledataSourceInterceptor { /** * O interceptador presta atenção especial à troca de fontes de dados antes de solicitar todas as classes de implementação de negócios. Como várias fontes de dados são usadas, é melhor chamar o mapeador apenas no *ServiceImpl. Caso contrário, ao ligar para uma tabela que não é uma fonte de dados padrão, uma exceção que não existe na tabela será relatada** @Param Junção* @Throws Throwable*/ @Before ("Execution (* com.xxxx.platform.Service ..*.* ServiceImpl. jun juntpoint.getTarget (). getClass (); String className = clazz.getName (); if (classutils.isassignable (clazz, proxy.class)) {ClassName = junção.getSignature (). getDecLaringTyPename (); } // Defina a fonte de dados do servidor com o nome da classe, caso contrário, o padrão é a fonte de dados em segundo plano if (ClassName.contains (". Serverra.")) {Multiplledatasource.setDataSourceKey (dbConstant.data_source_servera); } else if (className.Contains (". Serverb.")) {multiplledatasource.setDataSourceKey (dbConstant.data_source_serverb); } else {multiplledatasource.setDataSourceKey (dbConstant.data_source_op); }} /*** Quando a operação for concluída, se a fonte de dados atual for liberada, se não for liberada, ocorrerá um conflito de fonte de dados ao clicar com frequência. É uma tabela de outra fonte de dados, mas será executada para outra fonte de dados. O relatório não existe** @param junção** @THOWS THROTABLE*/ @AFTER ("Execução (* com.xxxx.service ..*.* ServiceImpl. }}Interceptar todos os métodos ServiceImpl, julgue qual função de fonte de dados pertence ao nome totalmente qualificado do método e selecione a fonte de dados correspondente. Após a conclusão da distribuição, libere a fonte de dados atual. Observe que eu usei a @Order, anotação de Spring, e falarei sobre isso a seguir, ao definir vários AOPs, a ordem é muito útil.
outro:
No começo, o projeto não introduziu transações, então tudo estava bem. Você pode acessar a fonte de dados correta sempre. Depois de ingressar no gerenciamento da transação da primavera, você não pode alternar dinamicamente a fonte de dados (parece que a transação não é eficaz, mas os dois não são válidos ao mesmo tempo). Mais tarde, descobri que o motivo era a ordem de execução da AOP, então usei a ordem da primavera mencionada acima:
Quanto menor a ordem, a execução é a primeira. Nesse ponto, você pode não apenas alternar as fontes de dados dinamicamente, mas também usar transações com sucesso (na mesma fonte de dados).
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.