Eu escrevi um blog "Spring+Mybatis+MySQL para criar uma estrutura de acesso ao banco de dados distribuída" antes de descrever como acessar vários bancos de dados através das fontes de dados dinâmicas da Spring+Mybatis. No entanto, a solução anterior possui algumas limitações (também descritas no blog original): aplica -se apenas a situações em que o número de bancos de dados é pequeno e fixo. Não há nada a fazer sobre a situação em que a dinâmica do banco de dados aumenta.
O esquema mencionado abaixo pode suportar adição e exclusão de banco de dados dinâmico, e o número é ilimitado.
Preparação do ambiente do banco de dados
Aqui está o MySQL como exemplo, primeiro os bancos de dados Build 3 localmente para teste. Deve -se notar que esta solução não limita o número de bancos de dados e suporta a implantação de diferentes bancos de dados em diferentes servidores. Conforme mostrado na figura, DB_PROJECT_001, DB_PROJECT_002, DB_PROJECT_003.
Construa um projeto de microsserviço de back -end Java
Crie um projeto MAVEN Spring Boot:
Config: classe de gerenciamento de configuração da fonte de dados.
DataSource: a lógica de gerenciamento de fonte de dados implementada por si mesma.
DBMGR: gerencia a relação de mapeamento entre a codificação do projeto e o nome do banco de dados e o nome (esta parte dos dados no projeto real é armazenada no cache Redis e pode ser adicionada e excluída dinamicamente).
Mapper: interface de acesso ao banco de dados.
Modelo: Modelo de Mapeamento.
REST: A interface RESTful lançada pelos microsserviços para o exterior, usada aqui para teste.
Application.yml: Configure os parâmetros JDBC do banco de dados.
Implementação detalhada de código
1. Adicione a configuração da fonte de dados
pacote com.elon.dds.config; importar javax.sql.dataSource; importar org.apache.ibatis.session.sqlSessionFactory; importar org.mybatis.spring.sqlsesFactoryBean; Importrg.mybatis.spring.annotation.mappers; org.springframework.beans.factory.annotation.qualifier; importar org.springframework.boot.autoconfigure.jdbc.datasourcebuilder; importação org.springframework.boot.context.properties.configuraturationProperties; importar org.springframework.context.annotation.bean; importar org.springframework.context.annotation.configuration; importar com.elon.dds.datasource.dynamicdataSource;/*** Gerenciamento de configuração da fonte de dados. * * @Author Elon * @Version 26 de fevereiro de 2018 * / @Configuration @MapperScan (BasEpackages = "com.elon.dds.mapper", value = "sqlsessionFactory") public class DataSourceConfig { /** * Crie fonte de dados com base nos parâmetros de conferência. Use subclasses derivadas. * * @return Data Source */ @Bean (name = "DataSource") @ConfigurationProperties (prefix = "spring.dataSource") public DataSource getDataSource () {DataSourceBuilder Builder = DataSourceBuilder.Crenete (); builder.type (dynamicdatasource.class); retorno construtor.build (); } /*** Crie uma fábrica de sessão. ** @param datasource fonte de dados* @return session factory*/ @Bean (name = "sqlSessionFactory") public sqlSessionFactory getSqlSessionFactory (@qualifier ("DataSource") DataSource DataSource) {sqlessionFactoryBean Bean = sqlsessfling bean.setDataSource (DataSource); tente {return bean.getObject (); } catch (Exceção e) {e.printStackTrace (); retornar nulo; }}} 2. Defina fontes de dados dinâmicas
1) Primeiro, adicione uma classe de identidade do banco de dados para distinguir diferentes acessos de banco de dados.
Como criamos bancos de dados separados para diferentes projetos, usamos a codificação do projeto como o índice do banco de dados. Os microsserviços suportam a simultaneidade multi-threaded e usam variáveis de encadeamento.
pacote com.elon.dds.datasource;/*** Classe de gerenciamento de identidade do banco de dados. Usado para distinguir diferentes bancos de dados conectados a fontes de dados. * * @author elon * @version 2018-02-25 */public class dbIdentifier {/** * Use diferentes codificações de projetos para distinguir bancos de dados */private static shreadlocal <string> projectCode = new ThreadLocal <string> (); public static string getProjectCode () {return ProjectCode.get (); } public static void setProjectCode (String Code) {ProjectCode.Set (Code); }}2) Uma DynamicDataSource é derivada do DataSource, onde a troca dinâmica de conexões de banco de dados é implementada
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;import org.apache.tomcat.jdbc.pool.poolproperties; importar com.elon.dds.dbmgr.projectdbmgr;/*** Definir classes derivadas de fonte de dados dinâmicas. Derivado da fonte de dados básica, implementada dinamicamente por si mesma. * * @Author Elon * @Version 2018-02-25 */public class DynamicDataSource estende DataSource {private static logger log = logManager.getLogger (dinâmicodatasource.class); /*** Este método é conectar -se a diferentes bancos de dados ao solicitar dados de diferentes projetos. */ @Override public Connection getConnection () {String ProjectCode = dbIdentifier.getProjectCode (); // 1. Obtenha a fonte de dados DataSource DDS = ddsholder.instance (). Getdds (ProjectCode); // 2. Crie if (dds == null) {try {DataSource newdds = initdds (ProjectCode); Ddsholder.instance (). Adddds (ProjectCode, newdds); } catch (ilegalargumentException | ilegalAccessException e) {Log.error ("Init Data Source Fail. ProjectCode:" + ProjectCode); retornar nulo; }} dds = ddsholder.instance (). getdds (ProjectCode); tente {return dds.getConnection (); } catch (sqLexception e) {e.printStackTrace (); retornar nulo; }} /*** Copie uma cópia com o objeto de dados atual como o modelo. * * @return dds * @throws ilegalAccessException * @THOWSOWS ILAGELARGUMENTEXCECTION */ Private DataSource Initdds (String ProjectCode) lança ilegalArgumentException, ilegalAccessException {DataSource DDS = new DataSource (); // 2. Copie as propriedades da propriedade PoolConfiguration PoolProperties = new PoolProperties (); Campo [] pfields = POLPPROPERTIES.CLASS.GETDECLAREDFIELDS (); para (Campo F: Pfields) {f.SetAccessible (true); Valor do objeto = f.get (this.getPoolProperties ()); tente {f.set (propriedade, valor); } catch (Exceção e) {log.info ("Valor definido falha. Nome do att:" + f.getName ()); continuar; }} dds.setPoolProperties (Propriedade); // 3. Defina o nome do banco de dados e IP (geralmente, a porta, o nome de usuário e a senha são fixados uniformemente) String urlFormat = this.geturl (); String url = string.format (urlformat, projectdbmgr.instance (). Getdbip (ProjectCode), ProjectDbmgr.instance (). GetDbname (ProjectCode)); dds.seturl (URL); retornar dds; }}3) Controle a liberação de conexão de dados através do DDStimer (liberação de fontes de dados não utilizadas que excederam o tempo especificado)
pacote com.elon.dds.dataSource; importar org.apache.tomcat.jdbc.pool.datasource;/*** Gerenciamento de temporizador de origem dinâmica de dados. A conexão do banco de dados que não tem acesso por um longo tempo está fechada. * * @Author Elon * @Version 25 de fevereiro de 2018 * /classe pública ddstimer { /** * período de tempo ocioso. As conexões de banco de dados que não foram acessadas por mais do que desta vez serão lançadas. O padrão é de 10 minutos. */ IndleperiodTime de longo prazo estático privado = 10 * 60 * 1000; / *** fonte de dados dinâmicos*/ private DataSource DDS; / *** Último tempo de acesso*/ privado LastUsetime; public ddstimer (DataSource DDS) {this.dds = dds; this.LastUSETime = System.CurrentTimEmillis (); } / *** Atualizado o tempo de acesso mais recente* / public void refreshtime () {lastusetime = system.currenttimemillis (); } /*** Detecte se a conexão de dados está fechada devido ao tempo limite. * * @return true - cronometrado; FALSO - não cronometrado*/ public boolean checkandClose () {if (system.currenttimemillis () - lastusetime> idleperiodtime) {dds.close (); retornar true; } retornar false; } public DataSource getdds () {return dds; }}4) Adicione o ddsholder para gerenciar diferentes fontes de dados e fornecer adição de fonte de dados e funções de consulta
pacote com.elon.dds.dataSource; importar java.util.hashmap; importar java.util.iterator; importar java.util.map; importar java.util.map.entry; import java.util.timer; import org.apache.tomcat.jdbc.pool.util.datas; * * @Author Elon * @Version 25 de fevereiro de 2018 * /public class Ddsholder { /** * Gerencie a lista dinâmica da fonte de dados. <Codificação do projeto, fonte de dados> */ mapa privado <string, ddstimer> ddsmap = new hashmap <string, ddstimer> (); / *** Fontes de dados não utilizadas periodicamente claras por meio de tarefas cronometradas*/ Timer estático privado clearIdLetask = new Timer (); estático {clearIdLetask.schedule (new clearIdLetImerTask (), 5000, 60 * 1000); }; private ddsholder () {} /** Obtenha objeto singleton* / public static ddsholder instance () {return ddsholderbuilder.instance; } /*** Adicione fonte de dados dinâmica. * * @param ProjectCode Project Encoding * @param dds dds */ public sincronizado void adddds (String ProjectCode, DataSource DDS) {ddstimer ddst = new ddstimer (dds); ddsmap.put (ProjectCode, DDST); } /** ddst.refreshtime (); retornar ddst.getdds (); } retornar nulo; } /*** Fontes de dados claras que foram cronometradas sem ninguém. */ public sincronizado void clearIdLedds () {iterator <entradas <string, ddstimer >> iter = ddsmap.entrySet (). iterator (); para (; Iter.hasnext ();) {Entry <String, ddstimer> Entry = iter.Next (); if (Entry.getValue (). checkandClose ()) {iter.remove (); }}} / *** Classe de artefato singleton* @Author Elon* @Version 26 de fevereiro de 2018* / classe estática privada ddsholderbuilder {private static ddsholder instância = new ddsholder (); }}5) Tarefa do timer O ClearIdLetimerTask é usado para limpar as fontes de dados ociosas regularmente
pacote com.elon.dds.dataSource; importar java.util.timertak;/*** limpar tarefas de conexão ociosa. * * @Author Elon * @Version 26 de fevereiro de 2018 */public class ClearIdLetImerTask estende o Timertosk {@Override public void run () {ddsholder.instance (). ClearIdLedds (); }}3. Gerencie o relacionamento de mapeamento de codificação de projeto com o banco de dados IP e o nome
pacote com.elon.dds.dbmgr; importar java.util.hashmap; importar java.util.map;/*** Gerenciamento de banco de dados do projeto. Fornece uma interface para consultar o nome do banco de dados e o IP com base na codificação do projeto. * @Author Elon* @Version 25 de fevereiro de 2018* /classe pública ProjectDBMGR { /*** Salvar a relação de mapeamento entre a codificação do projeto e o nome de dados. Aqui está o código difícil. No desenvolvimento real, esses dados relacionais podem ser salvos no cache Redis; * Adicionar um novo projeto ou excluir um projeto requer apenas atualizar o cache. Naquela época, a interface desta classe só precisa ser modificada para obter dados do cache. */ mapa privado <string, string> dbnamemap = new hashmap <string, string> (); /*** Salve a relação de mapeamento entre a codificação do projeto e o IP do banco de dados. */ mapa privado <string, string> dbipMap = new Hashmap <string, string> (); ProjectDBMGR () {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 Instância () {return ProjectDBMgrBuilder.Instance; } // No desenvolvimento real, ele é alterado para obter public String getDbname (String ProjectCode) {if (dbnamemap.containsKey (ProjectCode)) {return dbnamemap.get (ProjectCode); } retornar ""; } // No desenvolvimento real, mudamos para obter public String getDbip (String ProjectCode) {if (dbipmap.containskey (ProjectCode)) {return dbipmap.get (ProjectCode); } retornar ""; } classe estática privada ProjectDBMgrBuilder {private static ProjectDBMGR Instância = new ProjectDBMGR (); }} 4. Defina o mapeador para acesso ao banco de dados
pacote com.elon.dds.mapper; importar java.util.list; importar org.apache.ibatis.annotações.mapper; importar org.apache.ibatis.annotações.result; importache.apache.ibatis.annotações.Results; importação Órg.apache.anTeLet.AnTações. Definição da interface de mapeamento de Mybatis. ** @Author Elon* @Version 26 de fevereiro de 2018*/ @MapperPublic Interface UserMApper {/*** Consulta todos os dados do usuário* @return Lista de dados de usuários*/@Results (value = {@Result (Property = Userid ", Column =" Id "), @RESULT (Nome =" "" Column = "Age")}) @Select ("Selecione ID, nome, idade de tbl_user") Lista <suser> getUsers ();} 5. Defina o modelo de objeto de consulta
pacote com.elon.dds.model; public class Usuário {private int userID = -1; Nome da String Private = ""; private Int Age = -1; @Override public string tostring () {return "nome:" + nome + "| idade:" + idade; } public int getUserId () {return userID; } public void setUserID (int userId) {this.UserID = userID; } public string getName () {return name; } public void setName (nome da string) {this.name = name; } public int getage () {Age de retorno; } public void setage (int Age) {this.age = Age; }} 6. Defina a interface RESTful para consultar dados do usuário
pacote com.elon.dds.rest; importar java.util.list; importar org.springframework.beans.factory.annotation.autowired; importar org.springframework.web.bind.annotation.requestMapping; import org.springframework.webind.bind.bind.anTation.requestMapping; importação.springFramework.web.Bind.Bind.Bind.Bind.Bind.Bind.Bind.Bind.AnTation.requestMappation; org.springframework.web.bind.annotation.requestParam; importar org.springframework.web.bind.annotation.restcontroller; import com.elon.dds.mermapperce.dbentifier; interface. * * @Author Elon * @Version 26 de fevereiro de 2018 */ @RestController @requestmapping (value = "/user") classe pública wsuser {@autowired private UserMApper UsermApper; /*** Consulta todas as informações do usuário no projeto** @param ProjectCode Project Encoding* @return Lista de usuários*/@RequestMapping (value = "/v1/usuários", método = requestmethod.get) list <vigeiSejod) (@ReQuestParam (value "ProjectCode", exigir) (ProjectCode) (ProjectCode (@ReQuestParam (value "ProjectCode", requerw = true) string) {@ReQuestParam (value "ProjectCode", exigido) (ProjectCode) (ProjectCode (@ReQuestParam (valueCode ", requerwern) (ProjectCode); return userMApper.getUsers (); }}É necessário que o parâmetro do ProjectCode seja incluído em cada consulta.
7. Escreva o código de inicialização para o aplicativo de inicialização da primavera
pacote com.elon.dds; importar org.springframework.boot.springApplication; importar org.springframework.boot.autoconfigure.springbootApplication;/*** Olá, mundo! * */@SpringBootApplicationPublic Class App {public static void main (String [] args) {System.out.println ("Hello World!"); Springapplication.run (app.class, args); }} 8. Configure a fonte de dados no Application.yml
O nome do banco de dados e o nome do banco de dados são usados com %s. Alterne dinamicamente a consulta dados do usuário.
Spring: DataSource: URL: JDBC: MySQL: //%s: 3306/%s? useunicode = true & caracterEncoding = UTF-8 Nome de usuário: Raiz Senha: Driver-class-name: com.mysql.jdbc.driverLogging: Config: ClassPath: Log4j2.xml
Plano de teste
1. Consulte os dados do Project_001 e retorne normalmente
2. Consulte os dados do Project_002 e retorne normalmente
Resumir
O exposto acima é o código de implementação para acessar vários bancos de dados por meio de fontes de dados dinâmicas de configuração de inicialização da primavera. Espero que seja útil para todos. Se você tiver alguma dúvida, deixe -me uma mensagem e o editor responderá a todos a tempo. Muito obrigado pelo seu apoio ao site wulin.com!