Configuration record multiple data sources springboot

1. To achieve multi-data source switching, and certainly can not let springboot automatically configure data sources, so when you start, do not set the auto-configuration data, use the code in the startup class

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

The method of AbstractRoutingDataSource 2.Spring class determineTargetDataSource () is determined by the current thread data source for the method, the following source

protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }
resolvedDataSources is the total number of possible data sources, resolvedDefaultDataSource is the default data source, lenientFallback defaults to true, determineCurrentLookupKey () is an abstract method to determine the current lookup key for the transaction context to check the thread binding. 
So write a class that inherits AbstractRoutingDataSource, achieve determineCurrentLookupKey () is a must
public  class DynamicDataSource the extends AbstractRoutingDataSource { 

    Private  static the Map <String, String> = dataBaseDataSourceMapping new new the HashMap <> (); 

    Private  static List <String> Databases = new new the ArrayList <> (); 

    / ** 
     * data source routing, this method is used to select the data source generating the logical name 
     * / 
    @Override 
    protected Object determineCurrentLookupKey () {
         // Get the name of the data source shared thread 
        return DataSourceContextHolder.getDataSource (); 
    }    // obtained from a database <database: data source> set, and key database to obtain the data source used in accordance with
     

public static String getDataSource(String dataBase) { if (dataBaseDataSourceMapping.isEmpty()) { DataBaseDataSourceConfigMapper mapper = SpringUtils.getBean(DataBaseDataSourceConfigMapper.class); List<DataBaseDataSourceConfig> configs = mapper.findAllConfig(); configs.forEach(config -> dataBaseDataSourceMapping.put(config.getDataBase(), config.getDataSource())); } return dataBaseDataSourceMapping.get(dataBase); } //所有的数据源key 的枚举 public enum DataSourceType { DEFAULT("oghma"), OGHMA("oghma"), DW("dw"), QUARTZ("quartz"), CLS("cls"), OLD("old"); private String name; DataSourceType(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
DataSourceContextHolder store thread-local shared objects, ThreadLocal not go into effect
 
public class DataSourceContextHolder {
    private final static ThreadLocal<String> local = new ThreadLocal<>();

    public static void setDataSource(String name) {
        local.set(name);
    }

        public static String getDataSource() {
        return local.get();
    }

    public static void removeDataSource() {
        local.remove();
    }
}

3. The configuration of all the data sources, just to give a pattern, and specifying firm-dependent data source

application.properties
#分库 
spring.datasource.cls.url=XXXXX
spring.datasource.cls.username=XX
spring.datasource.cls.password=XX
spring.datasource.cls.driver-class-name=XXX

 
@Configuration
public class DataSourceConfig {
    @Value("${spring.datasource.cls.url}")
    private String clsUrl; 
    
    @Value("${spring.datasource.cls.username}")
    private String clsUserName;
    
    @Value("${spring.datasource.cls.password}")
    private String clsPassword;
    
    @Value("${spring.datasource.cls.driver-class-name}")
    private String clsDriver;
    
    @Bean(name = "clsDataSource")
    public DataSource dataSourceCls() {
        return DataSourceBuilder.create().type(HikariDataSource.class)
                        .driverClassName(clsDriver)
                        .url(clsUrl)
                        .username(clsUserName)
                        .password(clsPassword).build();
    }
@Bean(name
= "dynamicDataSource") @Primary public DataSource dataSource(){ DynamicDataSource dynamicDataSource = new DynamicDataSource(); DataSource oghma = dataSourceOghma(); DataSource dw = dataSourceDW(); DataSource quartz = dataSourceQuartz(); DataSource cls = dataSourceCls(); DataSource old = dataSourceOld(); //设置默认数据源 dynamicDataSource.setDefaultTargetDataSource(old); //配置多个数据源 Map<Object,Object> map = new HashMap<>(); map.put(DataSourceType.OGHMA.getName(), oghma); map.put(DataSourceType.DW.getName(), dw); map.put(DataSourceType.QUARTZ.getName(), quartz); map.put(DataSourceType.CLS.getName(), cls); map.put(DataSourceType.OLD.getName(), old); dynamicDataSource.setTargetDataSources(map); return dynamicDataSource; }
//事务管理器 @Bean
public PlatformTransactionManager txManager() { return new DataSourceTransactionManager(dataSource()); } }

Source has a simple configuration data writing, using@ConfigurationProperties(这种直接写在类中方法上,我没有弄成功,类上方法可以)

    @Bean (name = "clsDataSource" ) // read the configuration parameters in application.properties mapped to an object, prefix represents a prefix parameter 
    @ConfigurationProperties (prefix = " spring.datasource.cls " )
     public the DataSource dataSourceCls () {
         return DataSourceBuilder.create () Build ();. 
    }

Of course, be used @PropertySource (value = { "classpath: jdbc.properties"}), the configuration information from the file jdbc.properties application.properties moved.

4. write a comment, a section (a specifying method interception, intercept all the other methods of the specified class), cut and disposed to intercept the current method used to change which data source without annotations, the default data source. To prevent the transaction and annotations Meanwhile annotations using custom error occurs (when the cut surface of the first implementation of the transaction, the thread has not yet determined the source data, will complain), used here @Order specify a custom section executed before the transaction section.

@Documented 
@Retention (RetentionPolicy.RUNTIME) 
@Target ({ElementType.METHOD, ElementType.TYPE}) 
@Inherited 
public @ interface TargetDataSource { 

    / ** 
     * If the data source is determined with which to manually specify the data source name 
     * / 
    the DataSourceType value () default DataSourceType.OGHMA; 
    
    / ** 
     * can not be determined as the data source, a database is specified, the system automatically find the data source 
     * / 
    String dATABASE () default "" ; 
} 

@Aspect 
@Component 
the @Order ( . 1 ) 
 public  class DataSourceAspect { 

    Private Logger Logger = LoggerFactory.getLogger ( the this.getClass ()); 

    // entry points: the method of service classes inside the sub-packets (the packet and all packets in any arbitrary methods parameter names ending class Service)
     // @Pointcut ( "Execution ( com.topideal.supplychain-Service .. .. * * * (..)) ") 
    @Pointcut (" Annotation @ (com.topideal.supplychain.oghma.annotation.TargetDataSource) " )
     public  void dataSourcePointCut () { 
    } 

    @Before ( "dataSourcePointCut ()" )
     Private  void before (the JoinPoint Joinpoint) {
         the try { 
            Method, m = getMethod (Joinpoint);
             // if the annotation is present on the switching data sources m, switching is performed according to the annotations and the data source; if not, the default data source 
            IF (m! = null && m.isAnnotationPresent(TargetDataSource.class)) {
                TargetDataSource data = m.getAnnotation(TargetDataSource.class);
                if (!StringUtils.isEmpty(data.dataBase())) { // 配置了数据库,根据数据库得到数据源
                    String dataBase = (String) resolver(joinPoint, data.dataBase());
                    String dataSource = DynamicDataSource.getDataSource(dataBase);
                    if (!StringUtils.isEmpty(dataSource)) {
                        DataSourceContextHolder.setDataSource(dataSource);
                    } else {
                        throw new RuntimeException("dataBase : " + dataBase + "  没有数据源!");
                    }
                } else { // 指定数据源
                    DataSourceContextHolder.setDataSource(data.value().getName());
                }
                //logger.info("》》》》》》》 current thread " + Thread.currentThread().getName() + " add 【 " + data.value().getName() + " 】 to ThreadLocal");
            }
        } catch (Exception e) {
            DataSourceContextHolder.setDataSource(DataSourceType.DEFAULT.getName());
            e.printStackTrace();
        }
    } 

    // after performing the cut, the threads share the data source name Clear 
    @After ( "dataSourcePointCut ()" )
     public  void After (the JoinPoint Joinpoint) { 
        DataSourceContextHolder.removeDataSource (); 
    } 

    Private Method, getMethod (the JoinPoint PJP) throws Exception { 
        the Signature SIG = pjp.getSignature (); 
        MethodSignature MSIG = null ;
         IF ((SIG! the instanceof {MethodSignature))
             the throw  new new an IllegalArgumentException ( "the only method for annotation" ); 
        } the else {
            MSIG = (MethodSignature) SIG; 
            Object target = pjp.getTarget (); 
            Method, currentMethod = target.getClass () getDeclaredMethod (msig.getName (), msig.getParameterTypes ());.
             return currentMethod; 
        } 
    } 

    public Object Resolver (the JoinPoint Joinpoint, String STR) {
         IF (STR == null ) return  null ; 
        Object value = null ;
         IF (str.matches ( "# D * \\ \\ \\ {}")) { // If the name matches # {}, put the contents as variable
            String newStr = str.replaceAll("#\\{", "").replaceAll("\\}", "");
            if (newStr.contains(".")) { // 复杂类型
                try {
                    value = complexResolver(joinPoint, newStr);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                value = simpleResolver(joinPoint, newStr);
            }
        } else { //非变量
            value = str;
        }
        return value;
    }


    private Object complexResolver(JoinPoint joinPoint, String str) throws Exception {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        String[] names = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        String[] strs = str.split("\\.");

        for (int i = 0; i < names.length; i++) {
            if (strs[0].equals(names[i])) {
                Object obj = args[i];
                Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null);
                Object value = dmethod.invoke(args[i]);
                return getValue(value, 1, strs);
            }
        }

        return null;

    }

    private Object getValue(Object obj, int index, String[] strs) {
        try {
            if (obj != null && index < strs.length - 1) {
                Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null);
                obj = method.invoke(obj);
                getValue(obj, index + 1, strs);
            }
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private String getMethodName(String name) {
        return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
    }

    private Object simpleResolver(JoinPoint joinPoint, String str) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] names = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();

        for (int i = 0; i < names.length; i++) {
            if (str.equals(names[i])) {
                return args[i];
            }
        }
        return null;
    }

}

@Aspect
@Component
@Order(6)
public class DataSourceAspect2 {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("@within(com.topideal.supplychain.oghma.annotation.TargetDataSource)")
    public void dataSourcePointCut() {
    }

    @Before("dataSourcePointCut()")
    private void before(JoinPoint joinPoint) {
        try {
            Class<?> aClass = joinPoint.getTarget().getClass();
            TargetDataSource data = aClass.getAnnotation(TargetDataSource.class);
            if (data != null) {
                if(StringUtils.isEmpty (data.dataBase ())) { 
                    DataSourceContextHolder.setDataSource (data.value () getName ().); 
                } The else {
                     // the configuration database, according to the database to obtain the data source 
                    String the dataSource = DynamicDataSource.getDataSource (Data ; .dataBase ())
                     IF (! StringUtils.isEmpty (the dataSource)) { 
                        DataSourceContextHolder.setDataSource (the dataSource); 
                    } the else {
                         the throw  new new a RuntimeException ( "DATABASE:" data.dataBase + () + "! no data source" );  
                    }
                } 
            }

 
        } The catch (Exception E) { 
            DataSourceContextHolder.setDataSource (DataSourceType.DEFAULT.getName ()); 

        } 
    } 

    // after performing the cut, the data source name threads share emptying 
    @After ( "dataSourcePointCut ()" )
     public  void After (the JoinPoint Joinpoint) { 
        DataSourceContextHolder.removeDataSource (); 
    } 


}
DataSourceAspect direct write method may be, DataSourceAspect2 may annotation on an abstract class, assuming @TargetDataSource (the DataSourceType. DW) , the class inherits the abstract class uses the data source DW. Before a higher flexibility, specifically to see their favorite.

Guess you like

Origin www.cnblogs.com/carl-cn/p/12172962.html