Les exigences fonctionnelles sont que l'entreprise doit créer une grande plate-forme d'exploitation:
1. La plate-forme d'opération a sa propre base de données, en maintenant des fonctions de base telles que les utilisateurs, les rôles, les menus, les pièces et les autorisations.
2. La plate-forme de fonctionnement doit également fournir des opérations back-end d'autres services différents (Service A, Service B), et les bases de données du service A et du service B sont indépendantes.
Par conséquent, la plate-forme d'opération doit connecter au moins trois bibliothèques: Bibliothèque d'opération, bibliothèque et bibliothèque B, et espérer passer automatiquement à la source de données correspondante pour chaque demande de fonction (mon implémentation finale est de passer au niveau de méthode du service et de passer à la méthode de chaque couche DAO. Les fonctions de notre système sont relativement indépendantes les unes des autres).
Étape 1: configurer plusieurs sources de données
1. Définir la source de données:
La source de données que j'utilise est Druiddatasource d'Alibaba (c'est bien avec DBCP, c'est quoi que ce soit). La configuration est la suivante:
<! - op dataSource -> <bean id = "opdatasource" init-méthod = "init" destren-méthod = "close"> <propriété name = "url" value = "$ {db.master.url}" /> <propriété name = "username" value = "$ {db.master.user}" /> <propriété name = "mot de passe" value = "value =" $ {db.pass. <propriété name = "driverclassname" value = "$ {db.master.driver}" /> <propriété name = "initialSize" value = "5" /> <propriété name = "maxactive" value = "100" /> <propriété name = "minidle" value = "10" /> <propriété name = "maxwait" value = "x" 60000 "/> <propriété =" vaillante " name = "TestOnBorrow" value = "false" /> <propriété name = "testonreturn" value = "false" /> <propriété name = "test whileIdle" value = "true" /> <propriété name = "timebetweenictionrunsmillis" value = "600000" /> <propriété name = "MineVictableIdleMeLis" value = "300000" /> <<propriété name = "RemoveaBandon =" Value = "300000" /> <<propriété Named = "RemoveaDon =" Value = "300000" /> <<Propriété Nom = "RemoveaBandon =" Value = "300000" /> <<Property Named = "RemoveaDon =" Value = "300000" /> <fromy /> <propriété name = "removeAbandOnEdTimeout" value = "1800" /> <propriété name = "logabandoned" value = "true" /> <! - Configurez les filtres pour surveiller les statistiques intercept -> <propriété name = "filters" value = "config, megetat, wall, log4j2" /> <propriété name = "ConnectionProperties" value = "config.Decrypt = true" / </ Bean> " DataSource -> <bean id = "serverAdataSource" init-Method = "init" destrement-méthod = "close"> <propriété name = "url" value = "$ {db.servera.master.url}" /> <propriété name = "username" value = "$ {db.servera.master.user}" /> <propriété name = "mot de passe" mot de passe " Value = "$ {db.servera.master.password}" /> <propriété name = "driverclassname" value = "$ {db.servera.master.driver}" /> <propriété name = "initialSize" value = "5" /> <propriété name = "maxactive" value = "100" /> <propriété name = "MINIDLE" Value = "10" 10 "10" value = "60000" /> <propriété name = "validationQuery" value = "select 'x'" /> <propriété name = "testonBorrow" value = "false" /> <propriété name = "testonreturn" value = "false" /> <propriété name = "testonreturn" value = "false" /> <propriété name = "testonrern" value = "false" /> <prewawe name = "timebetweenvictionRunsmillis" value = "600000" /> <propriété name = "MineVictableIdleMemilis" value = "300000" /> <propriété name = "removeaBandoned" value = "true" /> <a propriété = "RemoveAbandOnEdTimeout" value = "1800" /> <propriété name = "logabandoned" value = "/ <! Surveillance des statistiques interceptions -> <propriété name = "filters" value = "config, Mergestat, wall, log4j2" /> <propriété name = "connectionproperties" value = "config.decrypt = true" /> </ank> <! - serverb datasource -> <bean id = "serverbdatasource" init-method = "init" détruiter-method = "close"> <url ". Value = "$ {db.serverb.master.url}" /> <propriété name = "username" value = "$ {db.serverb.master.user}" /> <propriété name = "mot de passe" value = "$ {db.serverb.master.password}" /> <propriété = "DriverClassname" value = "$ {db.erverb.Master.Master.DryDame" value = "$ {db.erverb. /> <propriété name = "initialSize" value = "5" /> <propriété name = "maxactive" value = "100" /> <propriété name = "minidle" value = "10" /> <propriété name = "maxwait" value = "60000" /> <propriété name = "validationQuery" value = "select 'x'" /> <propriété name = "testonborrow" value = "false" / false " value = "false" /> <propriété name = "test whileIdle" value = "true" /> <propriété name = "timebetweenvictionrunsmillis" value = "600000" /> <propriété name = "mineVictableIdleMemis" value = "300000" /> <propriété name = "removeaBandonEd" value "value" / "/> <propriété =" <propriété name = "logabandoned" value = "true" /> <! - Configurez les filtres pour la surveillance des statistiques intercept -> <propriété name = "filters" value = "config, mergestat, wall, log4j2" /> <propriété name = "ConnectionProperties" value = "config.decrypt = true" /> </ bean>J'ai configuré trois sources de données: OpdataSource (la source de données de la plate-forme de fonctionnement elle-même), ServerAdataSource et ServerBDataSource.
2. Configurer le multipledatasource
MultipledAnataSource est équivalent à un indicateur de proxy pour les trois sources de données ci-dessus. Lorsqu'il est vraiment combiné avec Spring / MyBatis, MultipledAtAsource et une utilisation de données de données configurés séparément ne sont pas différents:
<! - Spring Integration MyBatis: Configurez MultipledAtaSource -> <bean id = "SqlSessionFactory"> <propriété name = "DataSource" Ref = "MultipledataSource" /> <! - Scan Mapping.xml File -> <Property Name = "MAPPERLOCATIONS"> <Class> <Value> ClassPath *: / sqlmapperxml / *. <value> classpath *: / sqlmapperxml / * / *. xml </value> </ list> </ propriété> <propriété name = "configLocation" value = "classpath: xml / mybatis-cerfig.xml"> </ propriété> <propriété name = "TypeAlSespackage" varie Ref = "GlobalConfig" /> <propriété name = "Plugins"> <Barray> <! - Pagination Plugin Configuration -> <bean id = "PaginationInterceptor"> <propriété name = "dialectType" value = "MySQL" /> <propriété name = "Optimizetype" Value = "Alidruid" /> </Eplemfat -> <bean id = "MAPPERScannerConfigurer"> <! - Pour l'implémentation dynamique de l'interface DAO, vous devez savoir où se trouve l'interface -> <propriété name = "Basepackage" value = "com.xxx.platform.mapper" /> <Property Name = "SQLSessionFactoryBeanName" Value = "SQLSessionfactory"> </ Property> -> <bean id = "globalConfig"> <propriété name = "idType" value = "0" /> <propriété name = "dbcolumnUnderline" value = "true" /> </ bean> <! - Configuration de gestion des transactions multipledatasource -> <bean id = "transactionmanager"> <propriété name = "datasource" réf = "multipledatasource"> </ property> </ property>
Après avoir compris l'emplacement de MultipledAdAtasource, concentrons-nous sur la façon d'implémenter MultipledAdataSource. Le fichier de configuration est le suivant:
<bean id = "MultipledAtaSource"> <propriété name = "DefaultTargetDataSource" Ref = "OpdataSource" /> <propriété name = "TargetDataSources"> <map> <entrée key = "Opdatasource" Value-Ref = "OpDatasource" /> <Entry Key = "ServerAdatasource" Value-ref = "Serveradatasource" key = "serverbdatasource" value-ref = "serverbdatasource" /> </ map> </ propriété> </ban>
Le code Java implémenté est le suivant, et il n'y a pas besoin d'explications, et il est très clair en un coup d'œil:
import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource; / ** * * @classname: MultipledAtaSource * @description: configurer plusieurs sources de données <br> * @Author: Yuzhu.Peng * @date: 12 janvier 2018 à 4:37:25 PM * / Public Class PubligaSource ExtendSource at 4:37:25 PM * / Public Class PultipledArce ExtendSource ATSOR AbstractroutingDataSource {private static final threadLocal <string> dataSourceKey = new hheritableThreadLocal <string> (); public static void setDataSourceKey (String dataSource) {dataSourceKey.set (dataSource); } @Override Protected Object DetermentCurrentLookUpKey () {return dataSourceKey.get (); } public static void supprimeaSourceKey () {dataSourceKey.Remove (); }}Hérité de l'abstractroutingDataSource de Spring, implémente la méthode abstraite déterminantecurrentlookupkey. Cette méthode déterminera la source de données de source de données pour cette connexion chaque fois que la connexion de la base de données est obtenue. Vous pouvez voir le code Spring pour être clair:
/ * Obtenir la connexion * / Connexion publique getConnection () lève sqlexception {return déterminetargetDataSource (). GetConnection (); } Protected DataSource déterminceTargetDataSource () {assert.notnull (this.resolveddatasources, "DataSource Router non initialisé"); / * Le déterminancecurrentlookupkey ici est une interface abstraite, obtenant le nom de source de données spécifique * / Object LookupKey = déterminatCurrentlookupKey (); DataSource dataSource = (dataSource) this.resolveddatasources.get (lookupKey); if ((dataSource == null) && (((this.leentfallback) || (lookupkey == null)))) {dataSource = this.resolveddefaultDataSource; } if (dataSource == NULL) {Throw New illégalStateException ("Impossible de déterminer la clé de données cible pour la clé de recherche [" + LookupKey + "]"); } return dataSource; } / * Interface abstraite: c'est-à-dire l'interface implémentée par notre MultipledAdAtaSource * / objet abstrait protégé déterminant de déterminer la fourniture ();Étape 2: changez dynamiquement la source de données à chaque demande (niveau de méthode de service)
L'idée de mise en œuvre est d'utiliser l'idée AOP de Spring pour intercepter chaque appel de méthode de service, puis de changer dynamiquement la clé des données dans MultipledAnataSource en fonction du nom de chemin global de la méthode. Notre projet, pour les opérations de différents services, c'est-à-dire différentes bases de données, est indépendante les unes des autres. Il n'est pas recommandé d'appeler différentes sources de données dans la même méthode de service. De cette façon, nous devons déterminer dynamiquement si la fréquence de commutation doit être placée au niveau DAO, c'est-à-dire le niveau SQL. De plus, la gestion des transactions n'est pas pratique.
Examinons la mise en œuvre de l'AOP des sources de données de commutation dynamique:
Importer 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; * * @Author Yuzhu.Peng * @Since 2018-01-15 * / @ Aspect @ Order (1) Public Class MultipledataSourceInterceptor {/ ** * L'intercepteur accorde une attention particulière à la commutation de sources de données avant de demander toutes les classes de mise en œuvre commerciales. Étant donné que plusieurs sources de données sont utilisées, il est préférable d'appeler le mappeur uniquement dans * ServiceImpl. Sinon, lors de l'appel d'une table qui n'est pas une source de données par défaut, une exception qui n'existe pas dans le tableau sera signalée * * @param joinpoint * @throws throwable * / @before ("exécution (* com.xxxx.platform.service .. *. * ServiceIml. * (..))") joinpoint.getTarget (). getClass (); String className = Clazz.getName (); if (classutils.issignable (Clazz, proxy.class)) {className = joinpoint.getSignature (). getDeclagyTyPename (); } // Définissez la source de données Servera avec le nom de classe, sinon la valeur par défaut est la source de données en arrière-plan 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); }} / ** * Lorsque l'opération est terminée, si la source de données actuelle est libérée, si elle n'est pas publiée, un conflit de source de données se produira lors du clic fréquemment. Il s'agit d'un tableau d'une autre source de données, mais il fonctionnera vers une autre source de données. Le rapport n'existe pas * * @param joinpoint * @throws throwable * / @after ("EXECUTUTION (* com.xxxx.service .. *. * ServiceImpl. * (..))") public void supprimeataSoruce (joinpoint joinpoint) lance Throws {multipledataSource.ReMoveDataSourcekey (); }}Intercepter toutes les méthodes ServiceImpl, juger quelle fonction de source de données appartient au nom entièrement qualifié de la méthode, puis sélectionnez la source de données correspondante. Une fois la distribution terminée, relâchez la source de données actuelle. Notez que j'ai utilisé @Order, Annotation de Spring, et j'en parlerai ensuite, lors de la définition de plusieurs AOP, l'ordre est très utile.
autre:
Au début, le projet n'a pas introduit de transactions, donc tout allait bien. Vous pouvez accéder à la bonne source de données à chaque fois. Après avoir rejoint la gestion des transactions de Spring, vous ne pouvez pas changer dynamiquement la source de données (il semble que la transaction n'est pas efficace, mais les deux ne sont pas valides en même temps). Plus tard, j'ai constaté que la raison en était l'ordre d'exécution de l'AOP, j'ai donc utilisé l'ordre de ressort mentionné ci-dessus:
Plus l'ordre est petit, l'exécution est la première. À ce stade, vous pouvez non seulement changer de sources de données dynamiquement, mais également utiliser avec succès les transactions (dans la même source de données).
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.