配置文件(yml)spring: datasource: default-db-key: voidme multi-db: - voidme: driver-class-name: com.mysql.cj.jdbc.driver username: root password: root url: jdbc:mysql://192.168.42.153:3306/voidme?characterencoding=utf8&autoreconnect=true&failoverreadonly=false&maxreconnects=10&usessl=false - xcdef: driver-class-name: com.mysql.cj.jdbc.driver username: root password: root url: jdbc:mysql://192.168.42.153:3306/xcdef?characterencoding=utf8&autoreconnect=true&failoverreadonly=false&maxreconnects=10&usessl=falsemybatis: #1.classpath:只会到你的classes路径中查找找文件。 #2.classpath*:不仅会到classes路径,还包括jar文件中(classes路径)进行查找。 mapper-locations: classpath*:/mapper/**/*mapper.xml # mapper映射文件位置 type-aliases-package: com.**.entity # 实体类所在的位置 configuration: log-impl: org.apache.ibatis.logging.stdout.stdoutimpl #用于控制台打印sql语句 map-underscore-to-camel-case: true #开启将带有下划线的表字段 映射为驼峰格式的实体类属性
核心代码
dynamicdatasource这个类用于获取数据源的(核心)
package com.dynamicdatadource.dynamic;import org.springframework.beans.factory.annotation.value;import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource;public class dynamicdatasource extends abstractroutingdatasource { @value("${spring.datasource.default-db-key}") private string defaultdbkey; @override protected object determinecurrentlookupkey() { string currentdb = dynamicdatasourceservice.currentdb(); if (currentdb == null) { return defaultdbkey; } return currentdb; }}
dynamicdatasourceservice这个类是数据源切换工具,我们做了线程隔离了所以不用担心多线程数据源会混乱的问题
package com.dynamicdatadource.dynamic;import com.application.applicationcontextprovider;import org.slf4j.logger;import org.slf4j.loggerfactory;import org.springframework.boot.jdbc.datasourcebuilder;import javax.sql.datasource;import java.util.hashmap;import java.util.map;public class dynamicdatasourceservice { private static final logger log = loggerfactory.getlogger(dynamicdatasourceservice.class); private static final map<object, object> datasources = new hashmap<>(); private static final threadlocal<string> dbkeys = threadlocal.withinitial(() -> null); /** * 动态添加一个数据源 * * @param name 数据源的key * @param datasource 数据源对象 */ public static void adddatasource(string name, datasource datasource) { dynamicdatasource dynamicdatasource = applicationcontextprovider.getapplicationcontext().getbean(dynamicdatasource.class); datasources.put(name, datasource); dynamicdatasource.settargetdatasources(datasources); dynamicdatasource.afterpropertiesset(); log.info("添加了数据源:{}",name); } /** * @param name 数据源的key * @param driverclassname 驱动 * @param url 数据库连接地址 * @param username 数据库账户 * @param password 数据库密码 */ public static void adddatasource(string name, string driverclassname,string url,string username,string password) { datasourcebuilder<?> builder = datasourcebuilder.create(); builder.driverclassname(driverclassname); builder.username(username); builder.password(password); builder.url(url); adddatasource(name,builder.build()); log.info("添加了数据源:{}",name); } /** * 切换数据源 */ public static void switchdb(string dbkey) { dbkeys.set(dbkey); } /** * 重置数据源(切换为默认的数据源) */ public static void resetdb() { dbkeys.remove(); } /** * 获取当前数据源的key */ public static string currentdb() { return dbkeys.get(); }}
dynamicdatasourceconfig将数据源配置到springboot中和初始化mybaitis配置
package com.dynamicdatadource.dynamic;import lombok.data;import org.apache.ibatis.logging.log;import org.mybatis.spring.sqlsessionfactorybean;import org.springframework.boot.context.properties.configurationproperties;import org.springframework.context.annotation.bean;import org.springframework.context.annotation.configuration;import org.springframework.core.io.support.pathmatchingresourcepatternresolver;import org.springframework.jdbc.datasource.datasourcetransactionmanager;import org.springframework.transaction.platformtransactionmanager;import java.io.ioexception;import java.util.hashmap;import java.util.map;@configuration@configurationproperties(prefix = "mybatis")@datapublic class dynamicdatasourceconfig { private string mapperlocations; private string typealiasespackage; @data public class mybatisconfiguration{ private string logimpl; private boolean mapunderscoretocamelcase; } private mybatisconfiguration configuration=new mybatisconfiguration(); /** * 动态数据源 */ @bean public dynamicdatasource dynamicdatasource() { dynamicdatasource datasource = new dynamicdatasource(); map<object, object> targetdatasources = new hashmap<>(); datasource.settargetdatasources(targetdatasources); return datasource; } /** * 会话工厂mybaitis */ @bean public sqlsessionfactorybean sqlsessionfactorybean() throws ioexception, classnotfoundexception { org.apache.ibatis.session.configuration configuration = new org.apache.ibatis.session.configuration(); configuration.setmapunderscoretocamelcase(this.configuration.ismapunderscoretocamelcase()); //开启驼峰命名 configuration.setlogimpl((class<? extends log>) class.forname(this.configuration.getlogimpl())); //控制台打印sql日志 sqlsessionfactorybean sqlsessionfactorybean = new sqlsessionfactorybean(); sqlsessionfactorybean.setdatasource(dynamicdatasource()); sqlsessionfactorybean.setconfiguration(configuration); pathmatchingresourcepatternresolver resolver = new pathmatchingresourcepatternresolver(); sqlsessionfactorybean.setmapperlocations(resolver.getresources(mapperlocations)); sqlsessionfactorybean.settypealiasespackage(typealiasespackage); return sqlsessionfactorybean; } /** * 事务管理器 */ @bean public platformtransactionmanager transactionmanager() { return new datasourcetransactionmanager(dynamicdatasource()); }}
加载yml数据库配置类package com.dynamicdatadource.config;import com.dynamicdatadource.dynamic.dynamicdatasourceservice;import lombok.data;import org.springframework.boot.autoconfigure.jdbc.datasourceproperties;import org.springframework.boot.context.properties.configurationproperties;import org.springframework.boot.jdbc.datasourcebuilder;import org.springframework.stereotype.component;import javax.annotation.postconstruct;import javax.sql.datasource;import java.util.list;import java.util.map;import java.util.set;@component@data@configurationproperties(prefix = "spring.datasource")public class ymldatasourceprovider { private list<map<string, datasourceproperties>> multidb; private datasource builddatasource(datasourceproperties prop) { datasourcebuilder<?> builder = datasourcebuilder.create(); builder.driverclassname(prop.getdriverclassname()); builder.username(prop.getusername()); builder.password(prop.getpassword()); builder.url(prop.geturl()); return builder.build(); } public void initdatasource() { multidb.foreach(map -> { set<string> keys = map.keyset(); keys.foreach(key -> { datasourceproperties properties = map.get(key); datasource datasource = builddatasource(properties); dynamicdatasourceservice.adddatasource(key, datasource); }); }); } //在构造函数之后执行 @postconstruct public void init() { initdatasource(); }}
aop切换package com.dynamicdatadource.aop;import java.lang.annotation.elementtype;import java.lang.annotation.retention;import java.lang.annotation.retentionpolicy;import java.lang.annotation.target;@target({elementtype.method,elementtype.type})//作用:方法和类@retention(retentionpolicy.runtime)public @interface dynamicdatasourceanno { string key() default "";}
package com.dynamicdatadource.aop;import com.dynamicdatadource.dynamic.dynamicdatasourceservice;import org.apache.commons.lang.stringutils;import org.aspectj.lang.proceedingjoinpoint;import org.aspectj.lang.annotation.around;import org.aspectj.lang.annotation.aspect;import org.aspectj.lang.annotation.pointcut;import org.aspectj.lang.reflect.methodsignature;import org.springframework.stereotype.component;// 用于单独的请求或者类进行切换数据库@aspect@componentpublic class dynamicdatasourceaspect { @pointcut("@annotation(com.dynamicdatadource.aop.dynamicdatasourceanno)") public void dynamicdatasourceanno() { } @around("dynamicdatasourceanno()") public object dynamicdatasourceaspectaroundanno(proceedingjoinpoint joinpoint) { object object = null; try { methodsignature signature = (methodsignature)joinpoint.getsignature(); dynamicdatasourceanno dynamicdatasourceanno = signature.getmethod().getannotation(dynamicdatasourceanno.class); string key = dynamicdatasourceanno.key(); if (stringutils.isnotblank(key)) { //切换为指定数据库 dynamicdatasourceservice.switchdb(key); } object = joinpoint.proceed(); } catch (throwable e) { e.printstacktrace(); }finally { //还原为默认配置 dynamicdatasourceservice.resetdb(); } return object; } // 还可以扩展包路径切换}
效果运行程序之后,就会将数据源加入到数据源列表中了
扩展mysqldatasourceinitialize从数据库中将配置信息查询出来,然后动态添加到数据源列表中
package com.dao.config;import com.dao.datasourcedao;import com.dynamicdatadource.aop.dynamicdatasourceanno;import com.dynamicdatadource.dynamic.dynamicdatasourceservice;import com.entity.datasourceeneity;import org.springframework.beans.factory.initializingbean;import org.springframework.beans.factory.annotation.autowired;import org.springframework.boot.applicationarguments;import org.springframework.boot.applicationrunner;import org.springframework.stereotype.component;import javax.annotation.postconstruct;import javax.sql.datasource;import java.util.list;//从数据库中查询出全部的数据源,添加到数据源容器中/** * 表结构如下: * * create table `t_datasource` ( * `id` int(11) not null, * `key` varchar(255) character set utf8mb4 collate utf8mb4_general_ci not null comment '绑定的key,用于数据源的切换', * `url` text character set utf8mb4 collate utf8mb4_general_ci not null comment '数据库连接地址', * `username` varchar(255) character set utf8mb4 collate utf8mb4_general_ci not null comment '数据库用户名', * `password` varchar(255) character set utf8mb4 collate utf8mb4_general_ci not null comment '数据库密码', * `driverclassname` varchar(255) character set utf8mb4 collate utf8mb4_general_ci not null comment '数据库驱动', * `type` varchar(50) character set utf8mb4 collate utf8mb4_general_ci not null comment '数据库类型: mysql ,oracle,..', * `state` int(2) not null comment '是否可用: 1可用 ,2不可用', * primary key (`id`), * unique key `key` (`key`) * ) engine=innodb default charset=utf8mb4 collate=utf8mb4_general_ci; * * 上表要放入到默认数据源中的数据库里才行 */@componentpublic class mysqldatasourceinitialize implements applicationrunner { @autowired private datasourcedao datasourcedao; //项目启动后执行初始化数据源 @override public void run(applicationarguments args) throws exception { try { list<datasourceeneity> datasources = datasourcedao.getdatasources(); for (datasourceeneity datasource : datasources) { dynamicdatasourceservice.adddatasource(datasource.getkey(),datasource.getdatasource()); } } catch (exception e) { e.printstacktrace(); } }}
datasourceeneity实体类@datapublic class datasourceeneity { private int id; private string key; private string url; private string username; private string password; private string driverclassname; private string type; private int state; public datasource getdatasource() { datasourcebuilder<?> builder = datasourcebuilder.create(); builder.driverclassname(driverclassname); builder.username(username); builder.password(password); builder.url(url); return builder.build(); }}
以上就是springboot多数据源切换怎么实现的详细内容。