Los requisitos funcionales son que la empresa necesita construir una gran plataforma operativa:
1. La plataforma de operación tiene su propia base de datos, manteniendo funciones básicas como usuarios, roles, menús, piezas y permisos.
2. La plataforma de operación también debe proporcionar operaciones de fondo de otros servicios diferentes (Servicio A, Servicio B), y las bases de datos del Servicio A y el Servicio B son independientes.
Por lo tanto, la plataforma de operación debe conectar al menos tres bibliotecas: biblioteca de operaciones, una biblioteca y biblioteca B, y esperar cambiar automáticamente a la fuente de datos correspondiente para cada solicitud de función (mi implementación final es cambiar al nivel de método del servicio y cambiar al método de cada capa DAO. Las funciones de nuestro sistema son relativamente independientes entre sí).
Paso 1: Configurar múltiples fuentes de datos
1. Defina la fuente de datos:
La fuente de datos que uso es DruidDataSource de Alibaba (está bien con DBCP, esto es lo que sea). La configuración es la siguiente:
< <property name="driverClassName" value="${db.master.driver}" /> <property name="initialSize" value="5" /> <property name="maxActive" value="100" /> <property name="minIdle" value="10" /> <property name="maxWait" value="60000" /> <property name="validationQuery" value="SELECT 'x'" /> <propiedad name = "testOnBorrow" value = "false" /> <propiedad name = "testOnreturn" value = "false" /> <Property name = "testwhileIdle" value = "true" /> <propiedad name = "timebeteVictionRunsMillis value =" 600000 " /> <propiedad de propiedad =" mineVictIletIletImemeMillis Value = "300000" /> <<serperty nameeDing "nameOned" valor = "true" /> <propiedad name = "removeAbaBaBaLedTimeOut" value = "1800" /> <Property name = "logAbandoned" value = "true" /> <!-Configure filtros para monitorear las estadísticas interceptadas-> <propiedad = "filters" value = "config, mergestat, wall, log4j2" /> <name de la propiedad = "Conexión =" Value "Config." Configur.Con < value = "$ {db.servera.master.password}" /> <propiedad name = "controladorclassname" value = "$ {db.servera.master.driver}" /> <Property name = "inicial" value = "5" /> <propiedad = "maxactive" valor = "100" /> <name de propiedad = "minidle" value = "10" 10 " /10" /> 10 " /> value = "60000" /> <Property name = "ValidationQuery" value = "select 'x'" /> <Property name = "testOnBorrow" value = "false" /> <propiedad name = "testonreturn" value = "false" /> <propiedad name = "testOnreturn" value = "false" /> <propiedad name = "testonreturn" value = "false" /> <sperty name = "testHileLe" value "value" value "value" value "value" de propiedad " /" verdadera " name = "TimeBetWeeVictionRunsMillis" Value = "600000" /> <Property name = "mineVictableMeMillis" valor = "300000" /> <propiedad name = "removeAbeABeAnBaBaBaBaLEd" Value = "true" /> <Property Name = "RemoVeABaBaBaBaBaUned TIMEOut" Value = "1800" /> <Nombre de propiedad = "Logabander" Value = "True" /"verdadero" /"ALIMINETURA DEL MONTERURA FILTERS- FUNTERSE FUNTERSE FUNTERSE FUNTERSE FUNTERSE AL MONTERUENT statistics intercepts --> <property name="filters" value="config,mergeStat,wall,log4j2" /> <property name="connectionProperties" value="config.decrypt=true" /> </bean> <!-- serverB dataSource --> <bean id="serverBDataSource" init-method="init" destroy-method="close"> <property name="url" value = "$ {db.serverb.master.url}" /> <propiedad name = "username" value = "$ {db.serverb.master.user}" /> <propiedad name = "contraseña" value = "$ {db.serverb.master.password}" /> <name de propiedad = "controlador de controlador" Value = "$ {db.serb.sdriver}" /> <property name="initialSize" value="5" /> <property name="maxActive" value="100" /> <property name="minIdle" value="10" /> <property name="maxWait" value="60000" /> <property name="validationQuery" value="SELECT 'x'" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value = "false" /> <propiedad name = "testwhileidle" value = "true" /> <Property name = "timetweeVictionRunsMillis" value = "600000" /> <propiedad name = "mineVictableIdletImemillis" value = "300000" /> <propiedad de propiedad = "RemoveAned" Value = "True" /> <Property Name = "RemoveAleAnEdoneed Value" Value "ABRION" ALIMA "ALIMA" ALICIO "ALIMINADO" ABRIONES " <propiedad name = "logabandoned" value = "true" /> <!-Configurar filtros para monitorear las estadísticas intercepte-> <propiedad name = "filters" value = "config, mergestat, wall, log4j2" /> <Property name = "ConnectionProperties" value = "config.decrypt = true" /> < /bean>Configuré tres fuentes de datos: OpDataSource (la fuente de datos de la plataforma operativa en sí), ServerAdataSource y ServerBDataSource.
2. Configurar multipledataSource
MultipledataSource es equivalente a un proxy para las tres fuentes de datos anteriores. Cuando se combina realmente con Spring/MyBatis, MultipLedAtAsource y el uso de datos de datos configurados por separado no son diferentes:
<!-Spring Integration myBatis: configure multipledataSource-> <bean id = "sqlsessionFactory"> <propiedad name = "dataSource" ref = "multipledataSource"/> <!-escanee automáticamente el archivo mapping.xml-> <name de propiedad = "mapperLocations"> <list> <nalor> classpath*:/sqlmapperx/*. <value> classpath*:/sqlmapperxml/*/*. xml </valor> </list> </propiedad> <propiedad name = "configlocation" value = "classpath: xml/mybatis-config.xml"> </propiedad> <name de propiedad = "typealIspackage" Value = "Com.xxx.platform.model ref = "GlobalConfig"/> <Property Name = "Plugins"> <Arrray> <!-Configuración de complemento de paginación-> <bean id = "paginationInterceptore"> <Property name = "dialectType" valor = "mysql"/> <Property name = "optimizeType" value = "alidruid"/> </bean> </gray> -> <bean id = "mapperscannerconfigurer"> <!-Para la implementación dinámica de interfaz DAO, debe saber dónde está la interfaz-> <propiedad de propiedad = "basepackage" valor = "com.xxx.platform.mapper"/> <Property name = "sqlSessionFactoryBeanName" valor "valor =" sqlSession "> </propiedad> </</" </"n. -> <bean id = "globalconfig"> <propiedad name = "idtype" value = "0"/> <Property name = "dbColumnunderline" value = "true"/> </bean> <!-Configuración de administración de transacciones multipledatAsource
Después de comprender la ubicación de MultipLedAtAsource, centrémonos en cómo implementar MultipledatASource. El archivo de configuración es el siguiente:
<bean id = "multipledataSource"> <propiedad name = "defaultTargetDataSource" ref = "opDataSource" /> <propiedad name = "targetDataSources"> <map> <entry key = "opDataSource" value-ref = "opDataSource" /> <entry key = "serverAdataSource" value-ref = "ServerAdataSource" /entreten key = "ServerBDataSource" value-ref = "serverbdataSource"/> </s map> </property> </bean>
El código Java implementado es el siguiente, y no hay necesidad de demasiada explicación, y es muy claro de un vistazo:
importar org.springframework.jdbc.dataSource.lookup.AbstracTroutingDataSource;/** * * @classname: multipledataSource * @Description: configure múltiples fuentes de datos <br> * @author: yuzhu.peng * @Date: 12 de enero de 2018 a las 4:37:25 pm */pública clase múltiple múltiples múltiples múltiples múltiples múltiples múltiples múltiples. AbstractroutingDataSource {private static final ThreadLocal <String> dataSourceKey = new HereTablethEltLocal <String> (); public static void setDataSourceKey (String DataSource) {DataSourceKey.set (DataSource); } @Override Objeto protegido DetetInECRENTLOVEDUPKEY () {return dataSourceKey.get (); } public static void eliminATataSourceKey () {dataSourceKey.remove (); }}Heredado de AbstrutingDataSource de Spring, implementa el método abstracto DetetEdecurrentLoPKey. Este método determinará la fuente de datos de la fuente de datos para esta conexión cada vez que se obtenga la conexión de la base de datos. Puede ver que el código de primavera sea claro:
/*Get Connection*/ Public Connection getConnection () lanza SQLException {return DetdetEtArgetDataSource (). GetConnection (); } protegido dataSource DetdinTargetDataSource () {afirmo.notnull (this.resolvedDataSources, "enrutador de datos no inicializado"); /*El DetermineCurrentLlookUpkey aquí es una interfaz abstracta, obteniendo el nombre de origen de datos específico*/ Object LookupKey = DetetInECRENTLOWNUpKey (); DataSource DataSource = (DataSource) this.resOlvedDataSources.get (SeakUpKey); if ((dataSource == null) && (((this.lenientfallback) || (lookupkey == null)))) {dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) {tirar nueva ilegalStateException ("no se puede determinar el dataSource de destino para la tecla de búsqueda [" + seakupkey + "]"); } return dataSource; } /*Interfaz abstracta: es decir, la interfaz implementada por nuestro múltipleeurce* / protegido resumen DetdetInecUrnlowLoPLAUPKey ();Paso 2: cambie dinámicamente la fuente de datos cada solicitud (nivel de método de servicio)
La idea de implementación es utilizar la idea de AOP de Spring para interceptar cada llamada del método de servicio, y luego cambiar dinámicamente la clave de los datos en MultipLedAtAsource de acuerdo con el nombre general de la ruta del método. Nuestro proyecto, para las operaciones de diferentes servicios, es decir, diferentes bases de datos, es independiente entre sí. No se recomienda llamar a diferentes fuentes de datos en el mismo método de servicio. De esta manera, necesitamos determinar dinámicamente si la frecuencia de conmutación debe colocarse en el nivel DAO, es decir, el nivel SQL. Además, la gestión de transacciones no es conveniente.
Veamos la implementación de AOP de fuentes de datos de conmutación dinámica:
import java.lang.reflect.proxy; import org.apache.commons.lang.classutils; import org.spectj.lang.joinpoint; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.befefe; import org.springframework.annotation. ** ** ** ** ** dating stifting.bitte * @author yuzhu.peng * @since 2018-01-15 * / @aspecto @orden (1) clase pública multipledataSourceInterceptor { /** * El interceptor presta especial atención a cambiar fuentes de datos antes de solicitar todas las clases de implementación comercial. Dado que se utilizan múltiples fuentes de datos, es mejor llamar al mapeador solo en *ServiceImpl. De lo contrario, cuando llame a una tabla que no es una fuente de datos predeterminada, se informará una excepción que no existe en la tabla** @param unkenpoint* @throws showable*/ @bebefore ("ejecutor (* com.xxxx.platform.service ..*.* ServiceImpl.* (..))") public void setDataSoruce (JoinPoint se une unePoint) lanza lanzamiento {class <?> CLAZZ = CLAZZ = CLAZZ = unkenpoint.getTarget (). getClass (); Cadena className = clazz.getName (); if (classUtils.isassignable (Clazz, proxy.class)) {classname = unkePoint.getSignature (). getDeclaringTypename (); } // Establezca la fuente de datos de ServerA con el nombre de clase, de lo contrario, el valor predeterminado es la fuente de datos en segundo plano if (classname.contains (". Servera.")) {MultipledataSource.setDataSourceKey (dbconstant.data_source_servera); } else if (classname.contains (". serverb.")) {multipledataSource.setDataSourceKey (dbconstant.data_source_serverb); } else {multipledataSource.setDataSourceKey (dbconstant.data_source_op); }} /*** Cuando se completa la operación, si se libera la fuente de datos actual, si no se libera, se producirá un conflicto de fuente de datos al hacer clic con frecuencia. Es una tabla de otra fuente de datos, pero se ejecutará a otra fuente de datos. El informe no existe** @param unkenpoint* @throws showleable*/ @after ("ejecutor (* com.xxxx.service ..*.* ServiceImpl.* (..))") public void eliminatAteAsoruce (unión unión) lanza lando {multipledataSource.removedataSourceKey (); }}Interceptar todos los métodos de servicio de servicio, juzgar qué función de fuente de datos pertenece al nombre completamente calificado del método y luego seleccione la fuente de datos correspondiente. Después de completar la distribución, libere la fuente de datos actual. Tenga en cuenta que utilicé Spring's @Order, Annotation, y hablaré de eso a continuación, al definir múltiples AOP, el orden es muy útil.
otro:
Al principio, el proyecto no introdujo transacciones, por lo que todo estaba bien. Puede acceder a la fuente de datos correcta cada vez. Después de unir la gestión de transacciones de Spring, no puede cambiar dinámicamente la fuente de datos (parece que la transacción no es efectiva, pero las dos no son válidas al mismo tiempo). Más tarde, descubrí que la razón era la orden de ejecución de AOP, por lo que utilicé el orden de primavera mencionado anteriormente:
Cuanto menor sea el orden, la ejecución es la primera. En este punto, no solo puede cambiar las fuentes de datos dinámicamente, sino también usar con éxito transacciones (en la misma fuente de datos).
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.