欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Boot + Mybatis多数据源和动态数据源配置方法

程序员文章站 2023-12-09 14:30:03
网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种方式,记录一下配置方法供大家参考。 应用场景 项目需要同时连接两个不同的数据库a, b,并...

网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种方式,记录一下配置方法供大家参考。

应用场景

项目需要同时连接两个不同的数据库a, b,并且它们都为主从架构,一台写库,多台读库。

多数据源

首先要将spring boot自带的datasourceautoconfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源。在@springbootapplication注解中添加exclude属性即可:

@springbootapplication(exclude = {
  datasourceautoconfiguration.class
})
public class titanwebapplication {
 public static void main(string[] args) {
  springapplication.run(titanwebapplication.class, args);
 }
}

然后在application.properties中配置多数据源连接信息:

# titan库
spring.datasource.titan-master.url=jdbc:mysql://x.x.x.x:port/titan?characterencoding=utf-8
spring.datasource.titan-master.username=
spring.datasource.titan-master.password=
spring.datasource.titan-master.driver-class-name=com.mysql.jdbc.driver
# 连接池配置
# 省略
# 其它库
spring.datasource.db2.url=jdbc:mysql://x.x.x.x:port/titan2?characterencoding=utf-8
spring.datasource.db2.username=
spring.datasource.db2.password=
spring.datasource.db2.driver-class-name=com.mysql.jdbc.driver

由于我们禁掉了自动数据源配置,因些下一步就需要手动将这些数据源创建出来:

@configuration
public class datasourceconfig {
 @bean(name = "titanmasterds")
 @configurationproperties(prefix = "spring.datasource.titan-master") // application.properteis中对应属性的前缀
 public datasource datasource1() {
  return datasourcebuilder.create().build();
 }
 @bean(name = "ds2")
 @configurationproperties(prefix = "spring.datasource.db2") // application.properteis中对应属性的前缀
 public datasource datasource2() {
  return datasourcebuilder.create().build();
 }
}

接下来需要配置两个mybatis的sqlsessionfactory分别使用不同的数据源:

@configuration
@mapperscan(basepackages = {"titan.mapper"}, sqlsessionfactoryref = "sqlsessionfactory1")
public class mybatisdbaconfig {
 @autowired
 @qualifier("titanmasterds")
 private datasource ds1;
 @bean
 public sqlsessionfactory sqlsessionfactory1() throws exception {
  sqlsessionfactorybean factorybean = new sqlsessionfactorybean();
  factorybean.setdatasource(ds1); // 使用titan数据源, 连接titan库
  return factorybean.getobject();
 }
 @bean
 public sqlsessiontemplate sqlsessiontemplate1() throws exception {
  sqlsessiontemplate template = new sqlsessiontemplate(sqlsessionfactory1()); // 使用上面配置的factory
  return template;
 }
}

经过上面的配置后,titan.mapper下的mapper接口,都会使用titan数据源。同理可配第二个sqlsessionfactory:

@configuration
@mapperscan(basepackages = {"other.mapper"}, sqlsessionfactoryref = "sqlsessionfactory2")
public class mybatisdbbconfig {
 @autowired
 @qualifier("ds2")
 private datasource ds2;
 @bean
 public sqlsessionfactory sqlsessionfactory2() throws exception {
  sqlsessionfactorybean factorybean = new sqlsessionfactorybean();
  factorybean.setdatasource(ds2);
  return factorybean.getobject();
 }
 @bean
 public sqlsessiontemplate sqlsessiontemplate2() throws exception {
  sqlsessiontemplate template = new sqlsessiontemplate(sqlsessionfactory2());
  return template;
 }
}

完成这些配置后,假设有2个mapper titan.mapper.usermapper和other.mapper.rolemapper,使用前者时会自动连接titan库,后者连接ds2库。

动态数据源

使用动态数据源的初衷,是能在应用层做到读写分离,即在程序代码中控制不同的查询方法去连接不同的库。除了这种方法以外,数据库中间件也是个不错的选择,它的优点是数据库集群对应用来说只暴露为单库,不需要切换数据源的代码逻辑。

我们通过自定义注解 + aop的方式实现数据源动态切换。

首先定义一个contextholder, 用于保存当前线程使用的数据源名:

public class datasourcecontextholder {
 public static final logger log = loggerfactory.getlogger(datasourcecontextholder.class);
 /**
  * 默认数据源
  */
 public static final string default_ds = "titan-master";
 private static final threadlocal<string> contextholder = new threadlocal<>();
 // 设置数据源名
 public static void setdb(string dbtype) {
  log.debug("切换到{}数据源", dbtype);
  contextholder.set(dbtype);
 }
 // 获取数据源名
 public static string getdb() {
  return (contextholder.get());
 }
 // 清除数据源名
 public static void cleardb() {
  contextholder.remove();
 }
}

然后自定义一个javax.sql.datasource接口的实现,这里只需要继承spring为我们预先实现好的父类abstractroutingdatasource即可:

public class dynamicdatasource extends abstractroutingdatasource {
 private static final logger log = loggerfactory.getlogger(dynamicdatasource.class);
 @override
 protected object determinecurrentlookupkey() {
  log.debug("数据源为{}", datasourcecontextholder.getdb());
  return datasourcecontextholder.getdb();
 }
}

创建动态数据源:

/**
  * 动态数据源: 通过aop在不同数据源之间动态切换
  * @return
  */
 @bean(name = "dynamicds1")
 public datasource datasource() {
  dynamicdatasource dynamicdatasource = new dynamicdatasource();
  // 默认数据源
  dynamicdatasource.setdefaulttargetdatasource(datasource1());
  // 配置多数据源
  map<object, object> dsmap = new hashmap(5);
  dsmap.put("titan-master", datasource1());
  dsmap.put("ds2", datasource2());
  dynamicdatasource.settargetdatasources(dsmap);
  return dynamicdatasource;
 }

自定义注释@ds用于在编码时指定方法使用哪个数据源:

@retention(retentionpolicy.runtime)
@target({
  elementtype.method
})
public @interface ds {
 string value() default "titan-master";
}

编写aop切面,实现切换逻辑:

@aspect
@component
public class dynamicdatasourceaspect {
 @before("@annotation(ds)")
 public void beforeswitchds(joinpoint point){
  //获得当前访问的class
  class<?> classname = point.gettarget().getclass();
  //获得访问的方法名
  string methodname = point.getsignature().getname();
  //得到方法的参数的类型
  class[] argclass = ((methodsignature)point.getsignature()).getparametertypes();
  string datasource = datasourcecontextholder.default_ds;
  try {
   // 得到访问的方法对象
   method method = classname.getmethod(methodname, argclass);
   // 判断是否存在@ds注解
   if (method.isannotationpresent(ds.class)) {
    ds annotation = method.getannotation(ds.class);
    // 取出注解中的数据源名
    datasource = annotation.value();
   }
  } catch (exception e) {
   e.printstacktrace();
  }
  // 切换数据源
  datasourcecontextholder.setdb(datasource);
 }
 @after("@annotation(ds)")
 public void afterswitchds(joinpoint point){
  datasourcecontextholder.cleardb();
 }
}

完成上述配置后,在先前sqlsessionfactory配置中指定使用dynamicdatasource就可以在service中愉快的切换数据源了:

@autowired
 private useramodelmapper useramapper;
 @ds("titan-master")
 public string ds1() {
  return useramapper.selectbyprimarykey(1).getname();
 }
 @ds("ds2")
 public string ds2() {
  return useramapper.selectbyprimarykey(1).getname();
 }

总结

以上所述是小编给大家介绍的spring boot + mybatis多数据源和动态数据源配置方法,希望对大家有所帮助