Escribí un blog "Spring+MyBatis+MySQL para crear un marco de acceso a la base de datos distribuido" antes de describir cómo acceder a múltiples bases de datos a través de fuentes de datos dinámicas de configuración de Spring+MyBatis. Sin embargo, la solución anterior tiene algunas limitaciones (también descritas en el blog original): solo se aplica a situaciones en las que el número de bases de datos es pequeña y fija. No hay nada que hacer sobre la situación en la que aumenta la dinámica de la base de datos.
El esquema mencionado a continuación puede admitir la adición y eliminación de la base de datos dinámica, y el número es ilimitado.
Preparación del entorno de la base de datos
Aquí está MySQL como ejemplo, primero construya 3 bases de datos localmente para pruebas. Cabe señalar que esta solución no limita el número de bases de datos y admite la implementación de diferentes bases de datos en diferentes servidores. Como se muestra en la figura, db_project_001, db_project_002, db_project_003.
Construya un proyecto de microservicio de backend de Java
Cree un proyecto Maven de Boot Spring:
Configuración: clase de administración de configuración de fuente de datos.
DataSource: la lógica de gestión de la fuente de datos implementada por sí misma.
DBMGR: administra la relación de mapeo entre la codificación del proyecto y la IP y el nombre de la base de datos (esta parte de los datos en el proyecto real se almacena en el caché Redis y se puede agregar y eliminar dinámicamente).
Mapper: interfaz de acceso a la base de datos.
Modelo: modelo de mapeo.
REST: la interfaz RESTFUL lanzada por microservicios al exterior, utilizada aquí para pruebas.
Application.yml: configure los parámetros JDBC de la base de datos.
Implementación de código detallado
1. Agregar configuración de fuente de datos
paquete com.elon.dds.config; import javax.sql.datasource; import org.apache.ibatis.session.sqlsessionFactory; import org.mybatis.spring.sqlsessionFactoryBean; import og.mybatis.spring.annotation.mapperscan; importar; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.boot.autoconfigure.jdbc.datasourceBuilder; import org.springframework.boot.context.properties.configuationProperties; importar org.springframework.context.annotation.bean; importar org.springframework.context.annotation.configuration; import com.elon.dds.dataSource.dynamicDataSource;/*** Gestión de configuración de la fuente de datos. * * @author elon * @version 26 de febrero de 2018 * / @configuration @mapperscan (basepackages = "com.elon.dds.mapper", valor = "sqlsessionfactory") dataSourCeConfig { /** * Crear fuente de datos basada en los parámetros de configuración. Use subclases derivados. * * @return fuente de datos */ @Bean (name = "dataSource") @configurationProperties (prefix = "spring.dataSource") public dataSource getDataSource () {dataSourceBuilder Builder = dataSourceBuilder.create (); Builder.Type (DynamicDataSource.class); return builder.build (); } /*** Crear una fábrica de sesión. ** @param dataSource Fuente de datos* @return fábrica de sesión*/ @bean (name = "sqlSessionFactory") public sqlSessionFactory getsqlSessionFactory (@Qualifier ("dataSource") dataSource dataSource) {sqlSessionFactoryBean bean = new sqlSessionFactoryBean (); Bean.SetDataSource (DataSource); intente {return bean.getObject (); } catch (Exception e) {E.PrintStackTrace (); regresar nulo; }}} 2. Definir fuentes de datos dinámicos
1) Primero agregue una clase de identidad de base de datos para distinguir diferentes accesos de base de datos.
Dado que creamos bases de datos separadas para diferentes proyectos, utilizamos la codificación de proyectos como índice de la base de datos. Los microservicios admiten concurrencia multiproceso y usan variables de subprocesos.
paquete com.elon.dds.dataSource;/*** Clase de gestión de identidad de la base de datos. Utilizado para distinguir diferentes bases de datos conectadas a fuentes de datos. * * @author elon * @version 2018-02-25 */public class dbidentifier {/** * Use diferentes codificaciones de proyectos para distinguir bases de datos */private Static ThreadLocal <string> ProjectCode = new ThreadLocal <string> (); public static string getProjectCode () {return ProjectCode.get (); } public static void setProjectCode (código de cadena) {ProjectCode.set (código); }}2) Se deriva un DynamicDataSource de DataSource, donde se implementa la conmutación dinámica de las conexiones de la base de datos
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.poolproperties; import com.elon.dds.dbmgr.projectdbmgr;/*** Definir clases derivadas de la fuente de datos dinámica. Derivado de la fuente de datos básica, implementada dinámicamente por sí misma. * * @author elon * @version 2018-02-25 */public class DynamicDataSource extiende DataSource {private static logger log = logManager.getLogger (DynamicDataSource.class); /*** Este método es conectarse a diferentes bases de datos al solicitar datos de diferentes proyectos. */ @Override Public Connection getConnection () {String ProjectCode = dBidentifier.getProjectCode (); // 1. Obtenga la fuente de datos DataSource dds = ddsholder.Instance (). Getdds (ProjectCode); // 2. Create if (dds == null) {try {dataSource newdds = initdds (ProjectCode); Ddsholder.instance (). Adddds (ProjectCode, Newdds); } Catch (ilegalArgumentException | ilegalAccessException e) {log.error ("Init Data Fouring Fail. ProjectCode:" + ProjectCode); regresar nulo; }} dds = ddsholder.instance (). getdds (proyectocode); intente {return dds.getConnection (); } Catch (Sqlexception e) {E.PrintStackTrace (); regresar nulo; }} /*** Copie una copia con el objeto de datos actual como plantilla. * * @return dds * @throws ilegalAccessException * @throws ilegalargumentException */ private dataSource initdds (String ProjectCode) arroja ilegalargumentException, ilegalAccessException {DataSource dds = new DataSource (); // 2. Copie las propiedades de la propiedad de PoolConfiguration PoolProperties = new PoolProperties (); Campo [] pfields = PoolProperties.class.getDeclaredFields (); para (campo f: pfields) {f.setAccessible (true); Valor de objeto = f.get (this.getPoolProperties ()); intente {f.set (propiedad, valor); } Catch (Exception e) {log.info ("establecer el valor de valor. ATTR Nombre:" + F.getName ()); continuar; }} dds.setPoolProperties (propiedad); // 3. Establezca el nombre de la base de datos y la IP (generalmente, el puerto, el nombre de usuario y la contraseña se corrigen uniformemente) String urlFormat = this.getUrl (); String url = string.format (urlformat, proyectoDBMGR.Instance (). GetDBIP (ProjectCode), ProjectDBMGR.Instance (). GetDBName (ProjectCode)); dds.seturl (url); devolver DDS; }}3) Controle la versión de conexión de datos a través de DDSTIMER (liberación de fuentes de datos no utilizadas que han excedido el tiempo especificado)
paquete com.elon.dds.datasource; import org.apache.tomcat.jdbc.pool.datasource;/*** Gestión de temporizador de fuente de datos dinámico. La conexión de la base de datos que no tiene acceso durante mucho tiempo está cerrada. * * @author elon * @Version 25 de febrero de 2018 * /public class ddstimer { /** * Período de tiempo inactivo. Se lanzarán conexiones de bases de datos a las que no se ha accedido por más que este momento. El valor predeterminado es de 10 minutos. */ Private static long IdlePeriodtime = 10 * 60 * 1000; / *** Fuente de datos dinámico*/ DDDS DDDSOUNCE PRIVADO; / *** Último tiempo de acceso*/ Private Long LastUsetime; public ddstimer (DataSource dds) {this.dds = dds; this.lastusetime = System.CurrentTimemillis (); } / *** Actualizó el último tiempo de acceso* / public void refreshtime () {lastUsetime = system.currentTimemillis (); } /*** Detectar si la conexión de datos está cerrada debido al tiempo de espera. * * @return true - horario fuera; falso - no cronometrado*/ public boolean checkAndClose () {if (System.CurrentTimEmillis () - LastUsetime> IdlePeriodtime) {dds.close (); devolver verdadero; } return false; } public dataSource getdds () {return dds; }}4) Agregue DDSholder para administrar diferentes fuentes de datos y proporcionar funciones de adición de fuente de datos y consultas
paquete 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; importar org.apache.tomcat.jdbc.pool.dataSource;/*** dynamic data. * * @author elon * @version 25 de febrero de 2018 * /public class ddsholder { /** * Administre la lista dinámica de fuente de datos. <Codificación del proyecto, fuente de datos> */ MAP private <string, ddstimer> ddsmap = new HashMap <String, ddstimer> (); / *** Borrar periódicamente las fuentes de datos no utilizadas a través de tareas cronometradas*/ Temporizador estático privado ClearIdLetask = new Timer (); static {clearIdletask.schedule (new ClearIdletImerTask (), 5000, 60 * 1000); }; Private ddsholder () {} /** Get Singleton Object* / public static ddsholder instancia () {return ddsholderBuilder.Instance; } /*** Agregar fuente de datos dinámico. * * @param ProjectCode Project Coding * @param dds dds */ public sincronized void adddds (String ProjectCode, DataSource DDS) {ddstimer ddst = new Ddstimer (dds); ddsmap.put (ProjectCode, DDST); } / *** Fuente de datos dinámico de consulta** @Param ProjectCode Project Coding* @return dds* / public sincronizado dataSource getdds (String ProjectCode) {if (ddsmap.ContainsKey (ProjectCode)) {ddstimer ddst = dddsmap.get (proyectCode); ddst.refreshtime (); return ddst.getdds (); } return null; } /*** Borrar fuentes de datos que fueron programadas sin nadie. */ public sincronizado void clearIdledds () {iterator <Entry <String, ddstimer >> iter = ddsmap.Entryset (). iterator (); for (; iter.hasNext ();) {Entry <String, ddstimer> Entry = iter.next (); if (entrada.getValue (). checkAndClose ()) {iter.remove (); }}} / *** Singleton Artifact Clase* @Author Elon* @Version 26 de febrero de 2018* / Private static Class ddsholderBuilder {private static ddsholder instancia = nueva ddsholder (); }}5) La tarea del temporizador ClearIdletEmtaSk se utiliza para borrar las fuentes de datos inactivos regularmente
paquete com.elon.dds.dataSource; import java.util.timerTask;/*** Borrar tareas de conexión inactiva. * * @author elon * @Version 26 de febrero de 2018 */public class ClearIdletImerTask extiende TimeTask {@Override public void run () {ddsholder.instance (). clearIdledds (); }}3. Administrar la relación de mapeo de codificación del proyecto con la IP y el nombre de la base de datos
paquete com.elon.dds.dbmgr; import java.util.hashmap; import java.util.map;/*** Gestión de la base de datos del proyecto. Proporciona una interfaz para consultar el nombre de la base de datos y la IP basada en la codificación del proyecto. * @Author Elon* @Version 25 de febrero de 2018* /Public Class ProjectDBMGR { /*** Guarde la relación de mapeo entre la codificación del proyecto y el nombre de los datos. Aquí hay código duro. En el desarrollo real, estos datos relacionales se pueden guardar en el caché Redis; * Agregar un nuevo proyecto o eliminar un proyecto requiere solo actualizar el caché. En ese momento, la interfaz de esta clase solo debe modificarse para obtener datos del caché. */ private map <string, string> dbnamEmap = new HashMap <String, String> (); /*** Guarde la relación de mapeo entre la codificación del proyecto y la IP de la base de datos. */ private map <string, string> dbipmap = new Hashmap <String, String> (); ProjectDBMGR () privado {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"); } public static ProjectDBMGR instance () {return ProjectDBMGRBuilder.Instance; } // En el desarrollo real, se cambia para obtener una cadena pública getDbName (String ProjectCode) {if (dBnamEmap.ContainsKey (ProjectCode)) {return dbnamEmap.get (ProjectCode); } devolver ""; } // En el desarrollo real, cambiamos para obtener una cadena pública getDBIP (String ProjectCode) {if (dBipMap.ContainsKey (ProjectCode)) {return dBipMap.get (ProjectCode); } devolver ""; } Class estática privada ProjectDBMGRBuilder {private static ProjectDBMgr instancia = new ProjectDBMGR (); }} 4. Defina el mapeador para el acceso a la base de datos
paquete com.elon.dds.mapper; import java.util.list; importar org.apache.ibatis.annotations.mapper; importar org.apache.ibatis.annotations.result; import com.elon.dds.model.user;/*** Definición de interfaz de mapeo mybatis. ** @author elon* @Version 26 de febrero de 2018*/ @mapperpublic interfaz usermapper {/*** consulta todos los datos del usuario* @return Lista de datos de usuario*/@Results (valor = {@ResUtT (Propiedad = "UserId", columna = "id"), @Result (Propiedad = "Nombre", columna = "Nombre"), @Resid (Propiedad = "Age", "Age", columna "). }) @Select ("Seleccione ID, nombre, edad de TBL_USER") LIST <Ser User> getUsers ();} 5. Defina el modelo de objeto de consulta
paquete com.elon.dds.model; usuario de clase pública {private int userId = -1; Nombre de cadena privada = ""; private int Age = -1; @Override public String toString () {return "nombre:" + nombre + "| edad:" + edad; } public int getUserID () {return userId; } public void setUserID (int userId) {this.userID = userId; } public String getName () {nombre de retorno; } public void setName (nombre de cadena) {this.name = name; } public int getAge () {return Age; } public void setAge (int Age) {this.age = edad; }} 6. Defina la interfaz RESTFUL para consultar los datos del usuario
paquete com.elon.dds.rest; import java.util.list; import org.springframework.beans.factory.annotation.aUtowired; import org.springframework.web.bind.annotation.requestMapping; import og.springfrframework.web.bind.annotation.requestmethod; import; importar; import org.springframework.web.bind.annotation.requestparam; import org.springframework.web.bind.annotation.restcontroller; import com.elon.dds.datasource.dbidentiDier; import.elon.dds.mapper.usermapper; import com.elon.dds.modeleler;/outser; interfaz. * * @author elon * @Version 26 de febrero de 2018 */ @restcontroller @requestmapping (value = "/user") public class wsuser {@aUtowired private usermapper usermapper; /** * Query all user information in the project* * @param projectCode Project encoding* @return User list*/ @RequestMapping(value="/v1/users", method=RequestMethod.GET) public List<User> queryUser(@RequestParam(value="projectCode", required=true) String projectCode) { DBIdentifier.setProjectCode(projectCode); return usermapper.getusers (); }}Se requiere que el parámetro ProjectCode se incluya en cada consulta.
7. Escriba el código de inicio para la aplicación Spring Boot
paquete com.elon.dds; import org.springframework.boot.springapplication; import org.springframework.boot.autoconfigure.springbootapplication;/*** ¡Hola mundo! * */@SpringBootApplicationPublic Class App {public static void main (string [] args) {System.out.println ("¡Hola mundo!"); SpringApplication.run (App.Class, Args); }} 8. Configure la fuente de datos en Application.yml
La IP de la base de datos y el nombre de la base de datos se utilizan con %s. Cambiar dinámicamente en la consulta de datos del usuario.
Primavera: DataSource: URL: JDBC: MySQL: //%S: 3306/%s? UseUnicode = true y caracterSencoding = UTF-8 UserName: Root Password: Driver-Class-Name: com.mysql.jdbc.DriverLogging: config: classpath: log4j2.xmml
Plan de prueba
1. Consulte los datos de Project_001 y regrese normalmente
2. Consulte los datos de Project_002 y regrese normalmente
Resumir
El anterior es el código de implementación para acceder a múltiples bases de datos a través de fuentes de datos dinámicas de configuración de arranque de Spring. Espero que sea útil para todos. Si tiene alguna pregunta, déjame un mensaje y el editor responderá a todos a tiempo. ¡Muchas gracias por su apoyo al sitio web de Wulin.com!