Dynamic multiple data sources springboot

Because some special business scenarios, or the need to use multiple data sources of dynamic scenes based on customer strategy to do sub-library.
I see a lot of posts to write dynamic data sources can not support a service mix multiple data sources in the scene, and transaction control problems after the mix , use AbstractRoutingDataSource achieve, paste the key paragraphs

1:determineCurrentLookupKey 实现

public enum DbContextHolder {
    /**
     * 数据源枚举
     */
    Datasource;

    private static ThreadLocal<List<DbTypeEnum>> contextHolder = new ThreadLocal<>();

    /**
     * 设置数据源
     * @param dbTypeEnum
     */
    public void setDbType(DbTypeEnum dbTypeEnum) {
        if (null == contextHolder.get()) {
            contextHolder.set(new ArrayList<>());
        }
        contextHolder.get().add(dbTypeEnum);
    }
    /**
     * 取得当前数据源
     * @return
     */
    public String getDbType() {
        List<DbTypeEnum> dbTypeEnumList = contextHolder.get();
        return dbTypeEnumList.get(dbTypeEnumList.size() - 1).getValue();
    }
    /**
     * 清除当前数据源
     * @return
     */
    public void clear() {
        List<DbTypeEnum> dbTypeEnumList = contextHolder.get();
        dbTypeEnumList.remove(dbTypeEnumList.size() - 1);
    }
}

2:ThreadLocal<List<DbTypeEnum>>

A plurality of data sources to solve the call within the same service scenario
with Annotation @Db acting on the segmentation data source needs
DynamicDBAspect Code

@Aspect
@Order(0)
@Component
public class DynamicDBAspect {

    @Before(value = "@annotation(db)")
    public void before(JoinPoint point, Db db) {
        DbContextHolder.Datasource.setDbType(db.value());
    }

    @After(value = "@annotation(db)")
    public void after(JoinPoint point, Db db) {
        //在每次调用完成清除ThreadLocal<List<DbTypeEnum>> 最后一个数据源
        DbContextHolder.Datasource.clear();
    }

}

3: @Db implementation

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//继承Transactional并以非事务方式运行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public @interface Db  {
    DbTypeEnum value();
}

4: invocation:

service{
  a(){
      service1.invoke();
      service2.invoke();
      service3.invoke();
  }
}
service1{
  @Db(DbTypeEnum.a)
  invoke(){}
}
service2{
  @Db(DbTypeEnum.b)
  invoke(){}
}
service3{
  @Db(DbTypeEnum.c)
  invoke(){}
}

Note:

Why @Db to inherit @Transactional non-transactional way to run
1: If service.a use @Transactional modification, subsequent sqlConnection not instances, result in data source does not switch ( determineCurrentLookupKey ), we have mixed data source scene general inquiries only action, running in non-transactional way so it is reasonable to
2: use @Db modified service.method then @Transactional, will give priority to using the specified @Transactional

First wrote here, because of the recent take over of this project is based on the customer to do sub-libraries, will be the default data source entry in the request according to customerId, need to switch the scene just some of the common system call table class configuration, basically no control over the affairs , if there are multiple sources of data and requires a service call control transaction, Solutions are as follows:
1: the first data after the change for pre-check; call before performing fallible
2: can be controlled in a first non-transactional manner, the need rollback in the data logic to compensate

some large projects and services are basically micro multi data source does not exist in the scene, this transaction are also distributed transactions, compensation, etc. do

Reproduced in: https: //www.jianshu.com/p/c070371c484b

Guess you like

Origin blog.csdn.net/weixin_33862514/article/details/91190527