Reference juejin. Cn/post / 684490…

Background: The project has a functional requirement of multiple data sources. My idea is to configure a data source configuration library in the project, which contains the configuration of one table storage data source and the rest data sources are automatically generated according to the configuration table.

Implementation:

1. Build an environment

<? The XML version = "1.0" encoding = "utf-8"? > < project XMLNS = "http://maven.apache.org/POM/4.0.0" XMLNS: xsi = "http://www.w3.org/2001/XMLSchema-instance" Xsi: schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > The < modelVersion > 4.0.0 < / modelVersion > < groupId > org. Example < / groupId > < artifactId > FFAPIJson < / artifactId > < version > 1.0 - the SNAPSHOT < / version > < the parent > < groupId > org. Springframework. Boot < / groupId > The < artifactId > spring - the boot - starter - parent < / artifactId > < version > 2.2.2. RELEASE < / version > < relativePath / > <! -- lookup parent from repository --> </parent> <! <dependencies> <! --spring boot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency>  <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <! --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector- Java </artifactId> <scope>runtime</scope>  </dependency> <! --mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> < version > 3.3.0 < / version > < / dependency > < the dependency > < groupId > org. Springframework. Boot < / groupId > <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <! -- Hutool--> <dependency> <groupId>cn. Hutool </groupId> <artifactId> Hutool -all</artifactId> <version>5.5.9</version> </dependency> <! -- faster-json--> <! Alibaba </groupId> <artifactId> Fastjson </artifactId> <version>1.2.62</version> </dependency> </dependencies> </project>Copy the code

2. Configure multiple data sources

1. The entity class

@TableName("mydatasource") public class FfDataSource { private Integer id; @TableField("ip") private String url; private String port; @TableField("ff_schema") private String ffSchema; @TableField("driver_name") private String driverName; private String username; private String password; @TableField("key_name") private String keyName; @TableField("is_valid") private boolean isValid; private LocalDateTime createTime; private LocalDateTime updateTime; private String creator; private String updator; //------get set ------}Copy the code

2. Data table structure

CREATE TABLE 'mydatasource' (' id 'int NOT NULL AUTO_INCREMENT COMMENT '主键', 'IP' varchar(255) CHARACTER SET UTf8MB4 COLLATE UTf8MB4_0900_ai_ci NOT NULL COMMENT 'IP 127.0.0.1', 'port' varchar(255) CHARACTER SET UTf8MB4 COLLATE UTf8MB4_0900_ai_CI NOT NULL COMMENT '3306', 'ff_schema' vARCHar (255) CHARACTER SET UTf8MB4 COLLATE UTf8MB4_0900_ai_ci DEFAULT NULL COMMENT ' 'username' varchar(255) DEFAULT NULL COMMENT 'username ',' password 'varchar(255) DEFAULT NULL COMMENT' password ', 'driver_name' varchar(255) CHARACTER SET UTf8MB4 COLLATE UTf8MB4_0900_ai_ci DEFAULT NULL COMMENT 'driver ', 'key_name' varchar(255) CHARACTER SET UTf8MB4 COLLATE UTf8MB4_0900_ai_ci DEFAULT NULL COMMENT ' 'is_valid' tinyint DEFAULT '1' COMMENT ' DEFAULT value ', 'create_time' datetime DEFAULT NULL COMMENT 'create time ',' update_time 'datetime DEFAULT NULL COMMENT' update time ', 'creator' varchar(255) DEFAULT NULL COMMENT 'creator ',' updator 'varchar(255) DEFAULT NULL COMMENT' updator 'varchar ', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;Copy the code

3. Create the primary data source

@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) @Configuration @MapperScan(basePackages = "com.fivefu.core.mapper") @Order(5) public class DynamicDataSourceConfig { private static String jdbcUrl; private static String username; private static String password; private static String driver; @Bean(DataSourceConstants.DS_KEY_MASTER) public static DataSource masterDataSource() { return DataSourceBuilder.create().url(jdbcUrl).username(username).password(password).driverClassName(driver).build(); } @Bean @Primary public DataSource dynamicDataSource() { Map<Object, Object> datasourceMap = new HashMap<>(1); datasourceMap.put(DataSourceConstants.DS_KEY_MASTER, masterDataSource()); return new FFDynamicDS(masterDataSource(), datasourceMap); } @Value("${spring.datasource.master.jdbc-url}") public void setJdbcUrl(String jdbcUrl) { DynamicDataSourceConfig.jdbcUrl = jdbcUrl; } @Value("${spring.datasource.master.username}") public void setUsername(String username) { DynamicDataSourceConfig.username = username; } @Value("${spring.datasource.master.password}") public void setPassword(String password) { DynamicDataSourceConfig.password = password; } @Value("${spring.datasource.master.driver-class-name}") public void setDriver(String driver) { DynamicDataSourceConfig.driver = driver; }}Copy the code

4. Realize multiple data sources

Multiple source is essentially a collection of Map that need which data source from the Map, refer to the article about the more detailed, inheritance AbStractRoutingDataSource routing data sources (abstract) can be realized.

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { @Nullable private Map<Object, Object> targetDataSources; @nullable private Object defaultTargetDataSource; Private Boolean lenientFallback = true; private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup(); @Nullable private Map<Object, DataSource> resolvedDataSources; @Nullable private DataSource resolvedDefaultDataSource; //-----Copy the code

The @order constructor is added to the Spring environment via the @bean annotation. The @order constructor loads the default data source, loads the configuration of other data sources from the default data source, and adds it to the Map. So let’s implement

public class FFDynamicDS extends AbstractRoutingDataSource { private Map<Object,Object> backupTargetDataSources; public FFDynamicDS(DataSource defaultDataSource,Map<Object,Object> targetDataSOurce){ backupTargetDataSources = targetDataSOurce; super.setDefaultTargetDataSource(defaultDataSource); super.setTargetDataSources(backupTargetDataSources); super.afterPropertiesSet(); Public void addDataSource(List<FfDataSource> ffDataSources){for(FfDataSource item: ffDataSources){ this.backupTargetDataSources .put(item.getKeyName(), DataSourceUtil.makeNewDataSource(item)); } / / cover more than the original data sources super setTargetDataSources (backupTargetDataSources); / / this is equivalent to a data source set up / / AbstractRoutingDataSource implements InitializingBean / / AbstractRoutingDataSource afterPropertiesSet instantiated to call Super.afterpropertiesset (); } / / decides to lock the current data source, we use the data source key @ Override protected Object determineCurrentLookupKey () {return DynamicDataSourceContextHolder.getContextKey(); }}Copy the code

// Get other data sources

@Component @Order(10) public class DemoInitDataSource implements ApplicationRunner { @Autowired FfDataSourceService ffDataSourceService; public void initDataSource() { QueryWrapper<FfDataSource> queryWrapper = new QueryWrapper<>(); List<FfDataSource> list = ffDataSourceService.list(queryWrapper.eq("is_valid", 1)); DataSourceUtil.addDataSourceListToDynamic(list); } @override public void run(ApplicationArguments args) throws Exception {system.out.println (" start initializing the data source "); initDataSource(); }}Copy the code

Order(10) is intended to be slower than initializing the default data source, otherwise the request will fail before the data source is ready to load.

Project Address: gitee.com/own\_3\_0/f…