有时候在项目中会遇到需要连接两个数据库的情况。本文就结合pring和Mybati来讲下怎么使用双数据源(或者是多数据源)。

  背景知识介绍#

  本文中实现多数据源的关键是pring提供的AbtractRoutingDataource。这个类可以根据lookup key来实现底层数据源的动态转换。

  Copypublic abtract cla AbtractRoutingDataource extend AbtractDataource implement InitializingBean { @Nullable

  private Map targetDataource; @Nullable

  private Object defaultTargetDataource; private boolean lenientFallback = true; private DataourceLookup dataourceLookup = new JndiDataourceLookup(); @Nullable

  private Map reolvedDataource; @Nullable

  private Dataource reolvedDefaultDataource; public void etTargetDataource(Map targetDataource) { thi.targetDataource = targetDataource;

  } public void etDefaultTargetDataource(Object defaultTargetDataource) { thi.defaultTargetDataource = defaultTargetDataource;

  } public void etLenientFallback(boolean lenientFallback) { thi.lenientFallback = lenientFallback;

  } public void etDataourceLookup(@Nullable DataourceLookup dataourceLookup) { thi.dataourceLookup = (dataourceLookup != null ? dataourceLookup : new JndiDataourceLookup());

  } @Override

  public void afterPropertieet() { if (thi.targetDataource == null) { throw new IllegalArgumentException("Property 'targetDataource' i required");

  } thi.reolvedDataource = new HahMap<>(thi.targetDataource.ize()); thi.targetDataource.forEach((key, value) -> {

  Object lookupKey = reolvepecifiedLookupKey(key);

  Dataource dataource = reolvepecifiedDataource(value); thi.reolvedDataource.put(lookupKey, dataource);

  }); if (thi.defaultTargetDataource != null) { thi.reolvedDefaultDataource = reolvepecifiedDataource(thi.defaultTargetDataource);

  }

  } protected Object reolvepecifiedLookupKey(Object lookupKey) { return lookupKey;

  } protected Dataource reolvepecifiedDataource(Object dataource) throw IllegalArgumentException { if (dataource intanceof Dataource) { return (Dataource) dataource;

  } ele if (dataource intanceof tring) { return thi.dataourceLookup.getDataource((tring) dataource);

  } ele { throw new IllegalArgumentException( "Illegal data ource value - only [javax.ql.Dataource] and tring upported: " + dataource);

  }

  } @Override

  public Connection getConnection() throw QLException { return determineTargetDataource().getConnection();

  } @Override

  public Connection getConnection(tring uername, tring paword) throw QLException { return determineTargetDataource().getConnection(uername, paword);

  } @Override

  @uppreWarning("unchecked") public T unwrap(Cla iface) throw QLException { if (iface.iIntance(thi)) { return (T) thi;

  } return determineTargetDataource().unwrap(iface);

  } @Override

  public boolean iWrapperFor(Cla iface) throw QLException { return (iface.iIntance(thi) || determineTargetDataource().iWrapperFor(iface));

  }

  protected Dataource determineTargetDataource() {

  Aert.notNull(thi.reolvedDataource, "Dataource router not initialized");

  Object lookupKey = determineCurrentLookupKey();

  Dataource dataource = thi.reolvedDataource.get(lookupKey); if (dataource == null && (thi.lenientFallback || lookupKey == null)) {

  dataource = thi.reolvedDefaultDataource;

  } if (dataource == null) { throw new IllegaltateException("Cannot determine target Dataource for lookup key [" + lookupKey + "]");

  } return dataource;

  } @Nullable

  //一般只需要用户实现这个方法。

  protected abtract Object determineCurrentLookupKey();

  }郑州哪里割包皮好:http://www.zztongji120.com/郑州割包皮哪个医院好:http://www.zztongji120.com/郑州哪家医院包皮手术好:http://www.zztongji120.com/

  实现流程#

  tep1:实现一个自定义的AbtractRoutingDataource

  Copypublic cla DynamicDataource extend AbtractRoutingDataource { //这边定义了一个和线程绑定的ThreadLocal变量,用于存放需要使用的数据源的名称

  private tatic final ThreadLocal dataourceNameHolder = new ThreadLocal<>(); public DynamicDataource(Dataource defaultTargetDataource, Map targetDataource) { uper.etDefaultTargetDataource(defaultTargetDataource); uper.etTargetDataource(targetDataource); uper.afterPropertieet();

  } @Override

  //重写了AbtractRoutingDataource的determineCurrentLookupKey方法

  protected Object determineCurrentLookupKey() { return getDataource();

  } public tatic void etDataource(tring dataource) {

  dataourceNameHolder.et(dataource);

  } public tatic tring getDataource() { return dataourceNameHolder.get();

  } public tatic void clearDataource() {

  dataourceNameHolder.remove();

  }

  }

  tep2:实现一个AOP对ervice层方法进行AOP拦截,调用DynamicDataource中的ThreadLocal变量,将当前请求需要使用的数据源名称设置进去。

  Copy//定义一个Dataource注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Dataource { tring name() default "";

  }//这边再定义一个常量public interface DataourceName {

  tring FIRT = "firt";

  tring ECOND = "econd";

  }

  定义AOP处理Dataource注解

  Copy@Apect@Componentpublic cla DataourceApect implement Ordered { protected Logger logger = LoggerFactory.getLogger(getCla()); @Pointcut("@annotation(com.xx.yy.annotation.Dataource)") public void dataourcePointCut() {

  } @Around("dataourcePointCut()") public Object around(PeedingJoinPoint point) throw Throwable {

  Methodignature ignature = (Methodignature) point.getignature();

  Method method = ignature.getMethod();

  Dataource d = method.getAnnotation(Dataource.cla); //如果未指定数据源就使用第一个数据源

  if(d == null){

  DynamicDataource.etDataource(DataourceName.FIRT);

  logger.debug("et dataource i " + DataourceName.FIRT);

  }ele {

  DynamicDataource.etDataource(d.name());

  logger.debug("et dataource i " + d.name());

  } try { return point.peed();

  } finally {

  DynamicDataource.clearDataource();

  logger.debug("clean dataource");

  }

  } @Override

  public int getOrder() { return 1;

  }


  }

  tep3:对数据源进行配置

  Copy@Configurationpublic cla DynamicDataourceConfig { @Bean

  @ConfigurationPropertie("pring.dataource.druid.firt") public Dataource firtDataource(){ return DruidDataourceBuilder.create().build();

  } @Bean

  @ConfigurationPropertie("pring.dataource.druid.econd") public Dataource econdDataource(){

  Dataource dataource = DruidDataourceBuilder.create().build(); return dataource;

  } @Bean

  @Primary

  @DependOn(value = {"firtDataource","econdDataource"}) public DynamicDataource dataource(Dataource firtDataource,Dataource econdDataource) {

  Map targetDataource = new HahMap<>();

  targetDataource.put(DataourceName.FIRT, firtDataource);

  targetDataource.put(DataourceName.ECOND, econdDataource); return new DynamicDataource(firtDataource, targetDataource);

  }

  }

  使用#

  使用的时候非常简单,只需要在ervice层的方法上加上@Dataource注解就可以了。

  Copy@Dataource(name = DataourceName.ECOND)public tring electByInfoName(tring name){ //...}

  一些注意点#

  如果你使用了pageHelper等分页插件,请将方言设置成自动模式, autoRuntimeDialect: true

  Copypagehelper: reaonable: fale upportMethodArgument: true param: count=countql autoRuntimeDialect: true

  如果你使用了Druid数据源,并通过下面的形式创建数据源,要保障数据源的用户名和密码字段不为null。不然DruidDataourceWrapper这个Bean会检测这个字段的值,导致启动失败。

  Copy@Bean

  @ConfigurationPropertie("pring.dataource.druid.firt") public Dataource firtDataource(){ return DruidDataourceBuilder.create().build();

  } @Bean

  @ConfigurationPropertie("pring.dataource.druid.econd") public Dataource econdDataource(){

  Dataource dataource = DruidDataourceBuilder.create().build(); return dataource;