SpringBoot 自定义+动态切换数据源教程
程序员文章站
2023-12-31 18:19:04
目录1、添加maven依赖2、配置application.yml3、配置动态数据源4、配置数据源操作holder5、读取自定义数据源,并配置6、动态切换关键——aop进行切换7、使用1)、配置mapp...
1、添加maven依赖
<dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <scope>runtime</scope> </dependency> <dependency> <groupid>com.alibaba</groupid> <artifactid>druid</artifactid> <version>1.1.3</version> </dependency> <!--properties动态注入--> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-configuration-processor</artifactid> <optional>true</optional> </dependency> <!--springboot的aop--> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-aop</artifactid> </dependency>
2、配置application.yml
# 数据库访问配置 # 主数据源,默认的 druid: datasource: type: com.alibaba.druid.pool.druiddatasource driver-class-name: com.mysql.jdbc.driver url: jdbc:mysql://192.168.1.113:3306/test?useunicode=true&characterencoding=utf-8 username: root password: root # 下面为连接池的补充设置,应用到上面所有数据源中 # 初始化大小,最小,最大 initialsize: 5 minidle: 5 maxactive: 20 # 配置获取连接等待超时的时间 maxwait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timebetweenevictionrunsmillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minevictableidletimemillis: 300000 validationquery: select 1 from dual testwhileidle: true testonborrow: false testonreturn: false # 打开pscache,并且指定每个连接上pscache的大小 poolpreparedstatements: true maxpoolpreparedstatementperconnectionsize: 20 # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j # 通过connectproperties属性来打开mergesql功能;慢sql记录 connectionproperties: druid: stat: mergesql: true slowsqlmillis: 5000 # 合并多个druiddatasource的监控数据 #多数据源 mysql-db: datasource: names: logic,dao logic: driver-class-name: com.mysql.jdbc.driver url: jdbc:mysql://192.168.1.113:3306/test1?useunicode=true&characterencoding=utf-8 username: root password: root dao: driver-class-name: com.mysql.jdbc.driver url: jdbc:mysql://192.168.1.113:3306/test2?useunicode=true&characterencoding=utf-8 username: root password: root
3、配置动态数据源
import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource; /** * 动态数据源 * @author 陈梓平 * @date 2017/10/9. */ public class dynamicdatasource extends abstractroutingdatasource { @override protected object determinecurrentlookupkey() { return datasourceholder.getdatasource(); } }
4、配置数据源操作holder
import java.util.arraylist; import java.util.list; /** * 数据源操作 * @author 陈梓平 * @date 2017/10/9. */ public class datasourceholder { //线程本地环境 private static final threadlocal<string> contextholders = new threadlocal<string>(); //数据源列表 public static list<string> datasourceids = new arraylist<>(); //设置数据源 public static void setdatasource(string customertype) { contextholders.set(customertype); } //获取数据源 public static string getdatasource() { return (string) contextholders.get(); } //清除数据源 public static void cleardatasource() { contextholders.remove(); } /** * 判断指定datasrouce当前是否存在 * @param datasourceid * @return * @author shanhy * @create 2016年1月24日 */ public static boolean containsdatasource(string datasourceid){ return datasourceids.contains(datasourceid); } }
5、读取自定义数据源,并配置
import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.beans.mutablepropertyvalues; import org.springframework.beans.propertyvalues; import org.springframework.boot.autoconfigure.jdbc.datasourcebuilder; import org.springframework.boot.bind.relaxeddatabinder; import org.springframework.boot.bind.relaxedpropertyresolver; import org.springframework.context.environmentaware; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import org.springframework.core.convert.conversionservice; import org.springframework.core.convert.support.defaultconversionservice; import org.springframework.core.env.environment; import org.springframework.stereotype.component; import javax.sql.datasource; import java.util.hashmap; import java.util.map; /** * 数据源配置 * @author 陈梓平 * @date 2017/10/9. */ @component @configuration public class dynamicdatasourceconfig implements environmentaware { private static final logger logger = loggerfactory.getlogger(dynamicdatasourceconfig.class); // 默认数据源 private datasource defaultdatasource; // 属性值 private propertyvalues datasourcepropertyvalues; // 如配置文件中未指定数据源类型,使用该默认值 private static final object datasource_type_default = "org.apache.tomcat.jdbc.pool.datasource"; private conversionservice conversionservice = new defaultconversionservice(); private map<string, datasource> customdatasources = new hashmap<>(); @override public void setenvironment(environment environment) { initdefaultdatasource(environment); initotherdatasource(environment); } private void initotherdatasource(environment environment) { relaxedpropertyresolver propertyresolver = new relaxedpropertyresolver(environment, "mysql-db.datasource."); string dsprefixs = propertyresolver.getproperty("names"); for (string dsprefix : dsprefixs.split(",")) {// 多个数据源 map<string, object> dsmap = propertyresolver.getsubproperties(dsprefix+"."); datasource ds = builddatasource(dsmap); customdatasources.put(dsprefix, ds); databinder(ds, environment); } } private void initdefaultdatasource(environment environment) { // 读取主数据源 relaxedpropertyresolver propertyresolver = new relaxedpropertyresolver(environment, "druid.datasource."); map<string, object> dsmap = new hashmap<>(); dsmap.put("type", propertyresolver.getproperty("type")); dsmap.put("driver-class-name", propertyresolver.getproperty("driver-class-name")); dsmap.put("url", propertyresolver.getproperty("url")); dsmap.put("username", propertyresolver.getproperty("username")); dsmap.put("password", propertyresolver.getproperty("password")); defaultdatasource = builddatasource(dsmap); datasourceholder.datasourceids.add("ds1"); databinder(defaultdatasource, environment); } /** * 创建datasource * @param dsmap * @return * @author shanhy * @create 2016年1月24日 */ @suppresswarnings("unchecked") public datasource builddatasource(map<string, object> dsmap) { try { object type = dsmap.get("type"); if (type == null) type = datasource_type_default;// 默认datasource class<? extends datasource> datasourcetype; datasourcetype = (class<? extends datasource>) class.forname((string) type); string driverclassname = dsmap.get("driver-class-name").tostring(); string url = dsmap.get("url").tostring(); string username = dsmap.get("username").tostring(); string password = dsmap.get("password").tostring(); datasourcebuilder factory = datasourcebuilder.create().driverclassname(driverclassname).url(url) .username(username).password(password).type(datasourcetype); return factory.build(); } catch (classnotfoundexception e) { e.printstacktrace(); } return null; } /** * 为datasource绑定更多数据 * @param datasource * @param env * @author shanhy * @create 2016年1月25日 */ private void databinder(datasource datasource, environment env){ relaxeddatabinder databinder = new relaxeddatabinder(datasource); //databinder.setvalidator(new localvalidatorfactory().run(this.applicationcontext)); databinder.setconversionservice(conversionservice); databinder.setignorenestedproperties(false);//false databinder.setignoreinvalidfields(false);//false databinder.setignoreunknownfields(true);//true if(datasourcepropertyvalues == null){ map<string, object> rpr = new relaxedpropertyresolver(env, "druid.datasource.").getsubproperties("."); map<string, object> values = new hashmap<>(rpr); // 排除已经设置的属性 values.remove("type"); values.remove("driver-class-name"); values.remove("url"); values.remove("username"); values.remove("password"); datasourcepropertyvalues = new mutablepropertyvalues(values); } databinder.bind(datasourcepropertyvalues); } @bean(name = "datasource") public dynamicdatasource datasource() { dynamicdatasource dynamicdatasource = new dynamicdatasource(); // 默认数据源 dynamicdatasource.setdefaulttargetdatasource(defaultdatasource); // 配置多数据源 map<object, object> dsmap = new hashmap(5); dsmap.put("ds1", defaultdatasource); dsmap.putall(customdatasources); for (string key : customdatasources.keyset()) datasourceholder.datasourceids.add(key); dynamicdatasource.settargetdatasources(dsmap); return dynamicdatasource; } }
6、动态切换关键——aop进行切换
/** * 动态数据源注解 * @author 陈梓平 * @date 2017/10/9. */ @retention(retentionpolicy.runtime) @target({ elementtype.method }) public @interface ds { string name() default "ds1"; }
import com.chen.config.dynamicds.datasourceholder; import org.aspectj.lang.joinpoint; import org.aspectj.lang.annotation.after; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.core.annotation.order; import org.springframework.stereotype.component; /** * 设置数据源切面 * @author 陈梓平 * @date 2017/10/9. */ @aspect @order(-1)// 保证该aop在@transactional之前执行 @component public class dynamicdatasourceaspect { private static final logger logger = loggerfactory.getlogger(dynamicdatasourceaspect.class); @before("@annotation(ds)") public void changedatasource(joinpoint point, ds ds) throws throwable { string dsid = ds.name(); if (!datasourceholder.containsdatasource(dsid)) { logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getsignature()); } else { logger.debug("use datasource : {} > {}", ds.name(), point.getsignature()); datasourceholder.setdatasource(ds.name()); } } @after("@annotation(ds)") public void restoredatasource(joinpoint point, ds ds) { logger.debug("revert datasource : {} > {}", ds.name(), point.getsignature()); datasourceholder.cleardatasource(); } }
7、使用
1)、配置mapper
/** * @author 陈梓平 * @date 2017/10/9. */ public interface dynamicdsmapper { integer queryjournal(); string queryuser(); string querytype(); }
<?xml version="1.0" encoding="utf-8"?> <!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.chen.mapper.dynamicdsmapper"> <select id="queryjournal" resulttype="java.lang.integer"> select uid from journal </select> <select id="queryuser" resulttype="java.lang.string"> select name from user </select> <select id="querytype" resulttype="java.lang.string"> select parent from p_type </select> </mapper>
2)、配置service
/** * @author 陈梓平 * @date 2017/10/9. */ @service public class dynamicservciceimpl implements dynamicservcice { @autowired private dynamicdsmapper dynamicdsmapper; @ds() public integer ds1() { return dynamicdsmapper.queryjournal(); } @ds(name = "logic") public string ds2() { return dynamicdsmapper.queryuser(); } @ds(name = "dao") public string ds3() { return dynamicdsmapper.querytype(); } }
3)、单元测试调用
/** * 多数原测试 * @author 陈梓平 * @date 2017/10/9. */ @runwith(springrunner.class) @springboottest public class testdynamicds { private logger logger = loggerfactory.getlogger(testdynamicds.class); // @autowired private dynamicservcice dynamicservcice; @test public void test() { // integer integer = dynamicservcice.ds1(); // logger.info("integer:"+integer); // string ds2 = dynamicservcice.ds2(); // logger.info("ds2:"+ds2); string ds3 = dynamicservcice.ds3(); logger.info("ds3:"+ds3); } }
4)、测试结果
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。