J'ai écrit un blog "Spring + MyBatis + MySQL pour créer un cadre d'accès de base de données distribué" avant de décrire comment accéder à plusieurs bases de données via Spring + MyBatis Configuration Dynamic Data Sources. Cependant, la solution précédente a certaines limites (également décrites dans le blog d'origine): elle ne s'applique qu'aux situations où le nombre de bases de données est petit et fixe. Il n'y a rien à faire dans la situation où la dynamique de la base de données augmente.
Le schéma mentionné ci-dessous peut prendre en charge l'ajout de la base de données dynamique et la suppression, et le nombre est illimité.
Préparation de l'environnement de la base de données
Voici MySQL à titre d'exemple, créez d'abord 3 bases de données localement pour les tests. Il convient de noter que cette solution ne limite pas le nombre de bases de données et prend en charge le déploiement de différentes bases de données sur différents serveurs. Comme le montre la figure, db_project_001, db_project_002, db_project_003.
Construisez un projet de microservice Java Backend
Créer un projet Spring Boot Maven:
Config: Classe de gestion de la configuration de la source de données.
DataSource: la logique de gestion des sources de données implémentée par elle-même.
DBMGR: gère la relation de cartographie entre le codage du projet et la base de données IP et le nom (cette partie des données du projet réel est stockée dans le cache Redis et peut être ajoutée et supprimée dynamiquement).
Mappeur: interface d'accès à la base de données.
Modèle: modèle de cartographie.
REST: L'interface RESTful libérée par les microservices à l'extérieur, utilisée ici pour les tests.
Application.yml: Configurez les paramètres JDBC de la base de données.
Implémentation détaillée du code
1. Ajouter la configuration de la source de données
package com.elon.dds.config; import javax.sql.datasource; import org.apache.ibatis.session.sqlSessionFactory; import org.mybatis.spring.annotation.mapperscan; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.boot.autoconfigure.jdbc.DatasourceBuilder; import org.springframework.boot.context.properties.configurationproperties; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; Importer com.elon.dds.datasource.dynamicdatasource; / ** * Gestion de la configuration de la source de données. * * @author elon * @version 26 février 2018 * / @ configuration @ mapperscan (basepackages = "com.elon.dds.mapper", value = "sqlSessionFactory") classe publique DataSourceConfig {/ ** * Créer une source de données basée sur les paramètres de configuration. Utilisez des sous-classes dérivées. * * @Return Data Source * / @bean (name = "dataSource") @configurationproperties (prefix = "spring.datasource") public dataSource getDataSource () {dataSourceBuilder builder = dataSourceBuilder.create (); builder.type (dynamicdatasource.class); retour Builder.Build (); } / ** * Créez une usine de session. * * @Param DataSource Data Source * @return Session Factory * / @bean (name = "sqlSessionFactory") public sqlSessionFactory getSQLSessionFactory (@qualifier ("dataSource") dataSource dataSource) {sqlSessionFactoryBean Bean = new SqlSessionFaCtoryBean ();); bean.setDataSource (dataSource); essayez {return bean.getObject (); } catch (exception e) {e.printStackTrace (); retourner null; }}} 2. Définir les sources de données dynamiques
1) Ajoutez d'abord une classe d'identité de base de données pour distinguer différents accès de base de données.
Étant donné que nous avons créé des bases de données distinctes pour différents projets, nous avons utilisé le codage du projet comme index de la base de données. Les microservices prennent en charge la concurrence multi-thread et utilisent des variables de threads.
Package com.elon.dds.datasource; / ** * Classe de gestion de l'identité de base de données. Utilisé pour distinguer différentes bases de données connectées aux sources de données. * * @Author Elon * @version 2018-02-25 * / classe publique DbIdentifier {/ ** * Utilisez différents encodages de projet pour distinguer les bases de données * / private static <string> projectCode = new ThreadLocal <string> (); String statique public getProjectCode () {return projectCode.get (); } public static void setProjectCode (String code) {projectCode.set (code); }}2) Une dynamicdatasource est dérivée de DataSource, où la commutation dynamique des connexions de base de données est implémentée
import java.lang.reflect.field; import java.sql.connection; import java.sql.sqlexception; import org.apache.logging.log4j.logmanager; import org.apache.logging.log4j.logger; import org.apache.tomcat.jdbc.pool.datasource; org.apache.tomcat.jdbc.pool.poolProperties; import com.elon.dds.dbmgr.projectdbmgr; / ** * définir des classes dérivées de la source de données dynamiques. Dérivé de la source de données de base, implémentée dynamiquement par elle-même. * * @author elon * @version 2018-02-25 * / classe publique DynamicDataSource étend DataSource {private static logger log = LogManager.getLogger (dynamicdatasource.class); / ** * Cette méthode consiste à se connecter à différentes bases de données lors de la demande de données à différents projets. * / @Override Connexion publique getConnection () {String projectCode = dbIdentifier.getProjectCode (); // 1. Obtenez la source de données dataSource dds = ddsholder.instance (). GetDds (projectCode); // 2. Créer if (dds == null) {try {dataSource newdds = initdds (projectCode); Ddsholder.instance (). Adddds (projectCode, newdds); } Catch (illégalArgumentException | illégalaccessException e) {log.Error ("Init Data Source Fail. ProjectCode:" + ProjectCode); retourner null; }} dds = ddsholder.instance (). getDds (projectCode); essayez {return dds.getConnection (); } catch (sqlexception e) {e.printStackTrace (); retourner null; }} / ** * Copiez une copie avec l'objet de données actuel comme modèle. * * @return dds * @throws illégalaccessException * @throws illégalArgumentException * / private dataSource initdds (String projectcode) lève illégalArgumentException, illégalaccessException {dataSource dds = new DataSource (); // 2. Copier les propriétés de la propriété PoolConfiguration PoolProperties = new PoolProperties (); Champ [] pFields = poolProperties.class.getDeclaredFields (); pour (champ f: pfields) {f.setAccessible (true); Valeur objet = f.get (this.getPoolProperties ()); essayez {f.set (propriété, valeur); } catch (exception e) {log.info ("set value fail. Att name:" + f.getName ()); continuer; }} dds.setpoolProperties (propriété); // 3. Définissez le nom de la base de données et l'IP (généralement, le port, le nom d'utilisateur et le mot de passe sont uniformément fixes) UrlFormat de chaîne = this.getUrl (); String url = string.format (urlFormat, projectdbmgr.instance (). GetDBip (projectCode), projectDBMgr.instance (). GetDBName (projectCode)); dds.setUrl (URL); return dds; }}3) Contrôlez la version de la connexion des données via DDStimer (libération de sources de données inutilisées qui ont dépassé le temps spécifié)
package com.elon.dds.datasource; import org.apache.tomcat.jdbc.pool.datasource; / ** * Gestion du temporisateur de source de données dynamique. La connexion de la base de données qui n'a pas accès depuis longtemps est fermée. * * @author elon * @version 25 février 2018 * / classe publique ddstimer {/ ** * Période d'inactivité. Les connexions de base de données qui n'ont pas été accessibles depuis plus de temps seront publiées. La valeur par défaut est de 10 minutes. * / privé statique long idleperiodtime = 10 * 60 * 1000; / ** * Source de données dynamique * / DDS DataSource privé; / ** * Dernier temps d'accès * / privé long lastUseTime; public ddstimer (ddSource dds) {this.dds = dds; this.LastUseTime = System.CurrentTimemillis (); } / ** * Mise à jour du dernier temps d'accès * / public void refreshtime () {LastUseTime = System.CurrentTimeMillis (); } / ** * détecter si la connexion de données est fermée en raison du délai d'attente. * * @return true - timed out; false - non chronométré * / public booléen checkAndClose () {if (System.currenttimemillis () - LastUseTime> idlePeriodTime) {dds.close (); Retour Vrai; } return false; } public dataSource getDds () {return dds; }}4) Ajouter DDSholder pour gérer différentes sources de données et fournir des fonctions d'ajout de source de données et de requête
Package com.elon.dds.datasource; import java.util.hashmap; import java.util.iterator; import java.util.map; import java.util.map.entry; import java.util.timer; * * @author elon * @version 25 février 2018 * / classe publique DDSholder {/ ** * Gérer la liste des sources de données dynamiques. <Encodage du projet, source de données> * / carte privée <String, ddstimer> ddsmap = new HashMap <String, ddstimer> (); / ** * Effacez périodiquement les sources de données inutilisées par le biais de tâches chronométrées * / Timer statique privé ClearIdletAsk = new Timer (); statique {ClearIdletask.Schedule (new ClearIdleTiTTask (), 5000, 60 * 1000); }; private ddsholder () {} / * * Get singleton objet * / public static ddsholder instance () {return ddsholderbuilder.instance; } / ** * Ajouter une source de données dynamique. * * @param projectcode Project Encoding * @param dds dds * / public synchronisé void adddds (String projectcode, dataSource dds) {ddstimer ddsst = new ddstimer (dds); ddsmap.put (ProjectCode, ddst); } / ** * Query Dynamic Data Source * * @param ProjectCode Project Encoding * @return dds * / public synchronisé dataSource getDDS (String projectCode) {if (ddsmap.contitainsKey (projectCode)) {ddstimer ddsst = ddsmap.get (projectCode); ddst.refreshtime (); return ddst.getdds (); } return null; } / ** * Effacer les sources de données qui ont été chronométrées sans personne. * / public synchronisé void clearIdledds () {iterator <entrée <chaîne, ddstimer >> iter = ddsmap.entryset (). iterator (); pour (; iter.hasnext ();) {entrée <chaîne, ddstimer> entrée = iter.next (); if (entry.getValue (). checkAndClose ()) {iter.Remove (); }}} / ** * Singleton Artefact Class * @Author Elon * @version 26 février 2018 * / classe statique privée ddsholderBuilder {Instance ddsholder statique privée = new DDSholder (); }}5) La tâche de temporisation ClearIdleTimertask est utilisée pour effacer régulièrement les sources de données inactives
Package com.elon.dds.datasource; Importer Java.util.timertask; / ** * Effacer les tâches de connexion inactives. * * @author elon * @version 26 février 2018 * / classe publique ClearIdlemertask étend Timemertask {@Override public void run () {ddsholder.instance (). clearIdledDDD (); }}3. Gérer la relation de cartographie de l'encodage du projet avec IP de base de données et nom
package com.elon.dds.dbmgr; import java.util.hashmap; import java.util.map; / ** * Gestion de la base de données de projet. Fournit une interface pour requérir le nom de la base de données et IP basé sur le codage du projet. * @author elon * @version 25 février 2018 * / classe publique ProjectDBMGR {/ ** * Enregistrez la relation de mappage entre l'encodage du projet et le nom de données. Voici le code dur. Dans le développement réel, ces données relationnelles peuvent être enregistrées dans le cache Redis; * L'ajout d'un nouveau projet ou la suppression d'un projet nécessite uniquement la mise à jour du cache. À ce moment-là, l'interface de cette classe ne doit être modifiée que pour obtenir des données du cache. * / Private Map <String, String> dbNameMap = new HashMap <String, String> (); / ** * Enregistrez la relation de mappage entre le codage du projet et la base de données IP. * / Private Map <String, String> dbipMap = new HashMap <String, String> (); ProjectDBMGR privé () {dbnamemap.put ("project_001", "db_project_001"); dbnamemap.put ("project_002", "db_project_002"); dbnamemap.put ("project_003", "db_project_003"); dbipmap.put ("project_001", "127.0.0.1"); dbipmap.put ("project_002", "127.0.0.1"); dbipmap.put ("project_003", "127.0.0.1"); } Instance publique Static ProjectDBMGR () {return projectDBmgrBuilder.instance; } // Dans le développement réel, il est modifié pour obtenir la chaîne publique getDBName (String projectCode) {if (dbNamemap.ContainsKey (projectCode)) {return dbNameMap.get (projectCode); } retour ""; } // Dans le développement réel, nous changeons pour obtenir une chaîne publique getDBip (String projectCode) {if (dbipmap.contitainsKey (projectCode)) {return dbipmap.get (projectCode); } retour ""; } classe statique privée projectDBMGrBuilder {instance de projet statique privée = new ProjectDbmgr (); }} 4. Définissez le mappeur pour l'accès à la base de données
Package com.elon.dds.mapper; import java.util.list; import org.apache.ibatis.annotations.mapper; import org.apache.ibatis.annotations.result; import org.apache.ibatis.annotations.results; import org.apache.ibatis.annotations. Définition de l'interface de mappage MyBatis. * * @Author Elon * @version 26 février 2018 * / @ Mappepublic Interface UserMapper {/ ** * Recherchez toutes les données utilisateur * @return Liste des données utilisateur * / @Results (valeur = @Sresult (propriété = "utilisateur", Column = "Id"), @Result (Agel = "Name", Column = "Name"), @Result (Property = "Age", "Age"), ")"), @Result (Property = "Age", ")"), "),"), @Result @Select ("SELECT ID, nom, âge from tbl_user") Liste <User> getUsers ();} 5. Définissez le modèle d'objet de requête
package com.elon.dds.model; public class user {private int userId = -1; Nom de chaîne privée = ""; Int privé = -1; @Override public String toString () {return "name:" + name + "| Âge:" + age; } public int getUserId () {return userId; } public void SetUserId (int userId) {this.UserId = userId; } public String getName () {Nom de retour; } public void setName (string name) {this.name = name; } public int getage () {return âge; } public void Setage (int Age) {this.age = age; }} 6. Définissez l'interface RESTful pour interroger les données des utilisateurs
package com.elon.dds.rest; import java.util.list; import org.springframework.beans.factory.annotation.autowired; import org.springframework.web.bind.annotation.anquestmapping; import org.springframework.webind.annotation.requestmethod; import org.springframework.web.bind.annotation.requestParam; import org.springframework.web.bind.annotation.restController; Importer com.elon.dds.datasource.dbidentifier; import com.elon.dds.mapper.sermapper; Interface d'importation. * * @author elon * @version 26 février 2018 * / @ restController @ requestmapping (value = "/ user") public class wsuser {@Autowired private userapper Userperper; / ** * requête toutes les informations utilisateur du projet * * @param projectcode Project Encoding * @return utilisateur Liste * / @RequestMapping (value = "/ v1 / utilisateurs", méthode = requestMethod.get) public List <Derser> QueryUser (@RequestParam (value = "projectCode", requise = true) String); retourner userMapper.getUser (); }}Il est nécessaire que le paramètre ProjectCode soit inclus dans chaque requête.
7. Écrivez le code de démarrage de l'application Spring Boot
package com.elon.dds; import org.springframework.boot.springApplication; import org.springframework.boot.autoconfigure.springbootapplication; / ** * bonjour world! * * / @ SpringbootApplicationPublic class App {public static void main (String [] args) {System.out.println ("Hello World!"); SpringApplication.Run (app.class, args); }} 8. Configurer la source de données dans Application.yml
L'IP de la base de données et le nom de la base de données sont utilisés avec% s. Passer dynamiquement à interroger les données utilisateur.
Spring: DataSource: URL: jdbc: mysql: //% s: 3306 /% s? useunicode = true & worseencoding = utf-8 nom d'utilisateur: mot de passe root: driver-class-name: com.mysql.jdbc.driverlogging: config: ClassPath: Log4j2.xml
Plan d'essai
1. Interrogez les données de Project_001 et retournez normalement
2. Interrogez les données de Project_002 et retournez normalement
Résumer
Ce qui précède est le code d'implémentation pour accéder à plusieurs bases de données via les sources de données dynamiques de configuration de démarrage Spring. J'espère que ce sera utile à tout le monde. Si vous avez des questions, veuillez me laisser un message et l'éditeur répondra à tout le monde à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!