This is the second day of my participation in the First Challenge 2022

Introduction to the

Based on Shardingsphere JDBC 5.0.0 version, Sharding branch library is used to realize the daily use of multi-data sources in the database, combined with Spring Boot and Mybatis Plus

Data Source Requirements

The database initial statement is as follows:

create database demo1;
create database demo2;

create table `demo1`.table1 (
    id int
);

create table `demo2`.table2 (
    id int
);

create table `demo1`.sharding_table (
    id int
);

create table `demo2`.sharding_table (
    id int
);

insert into `demo1`.sharding_table (id) values(1);
insert into `demo2`.sharding_table (id) values(1);
Copy the code

Database 1 has tables table1 and sharding_table

Database 2 has tables: table2, sharding_table

Requirements are as follows:

  • When accessing table Table1, access the database Demo1
  • When accessing table Table2, access the database Demo2
  • When accessing the sharding_table table, the corresponding data is accessed according to the custom incoming parameters. For this article, the data source to be accessed is stored in ThreadLocal, and the corresponding data source is accessed after being retrieved

Key code Examples

GitHub address: github.com/lw124392545…

Defining data sources

To configure ShardingSphere JDBC data source, the key code is as follows:

Table table1 accesses data source DB0 and table table2 accesses data source db1

# shardingSphere branch library Settings
shardingsphere:
  Configure the real data source
  datasources:
    # database 1
    db0:
      jdbcurl: The ${DB1_URL: JDBC: mysql: / / 127.0.0.1:3306 / not? useUnicode=true&serverTimezone=UTC}
      username: ${DB1_USER:root}
      password: ${DB1_PASS:root}
    # database 2
    db1:
      jdbcurl: The ${DB2_URL: JDBC: mysql: / / 127.0.0.1:3306 / demo2? useUnicode=true&serverTimezone=UTC}
      username: ${DB2_USER:root}
      password: ${DB2_PASS:root}
  rules:
    table1: db0
    table2: db1
Copy the code

If you use yamL file configuration of ShardingSphere, you have not found the way to use environment variables, so it is not convenient to modify, so you use Java code directly to configure

@Slf4j
@Configuration
public class ShardingDataSourceMybatisPlusConfig extends MybatisPlusAutoConfiguration {

    private final MultipleDbConfig multipleDbConfig;

    @Primary
    @Bean("dataSource")
    public DataSource getDataSource(a) throws SQLException {
        // Configure the real data source
        Map<String, MultipleDbConfig.DbSource> dbs = multipleDbConfig.getDatasources();
        Map<String, DataSource> dataSourceMap = new HashMap<>(dbs.size());
        for (String dbName: dbs.keySet()) {
            MultipleDbConfig.DbSource dbConfig = dbs.get(dbName);
            HikariDataSource dataSource = new HikariDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl(dbConfig.getJdbcUrl());
            dataSource.setUsername(dbConfig.getUsername());
            dataSource.setPassword(dbConfig.getPassword());
            dataSourceMap.put(dbName, dataSource);
        }

        // Configure sharding rules
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

        Table table1 to data source DB0, table table2 to data source db1
        Map<String, String> rules = multipleDbConfig.getRules();
        for (final String table: rules.keySet()) {
            // configure to add t_ORDER table rules
            final String actualDataNodes = String.join(".", rules.get(table), table);
            shardingRuleConfig.getTables().add(new ShardingTableRuleConfiguration(table, actualDataNodes));
        }

        // To configure sharding_table access, you need to customize the implementation of the sub-database and sub-table algorithm
        ShardingTableRuleConfiguration ShardingTableRuleConfiguration = new ShardingTableRuleConfiguration("sharding_table"."db${0.. 1}.sharding_table");
        shardingRuleConfig.setDefaultDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("id"."customDbSharding"));
        shardingRuleConfig.setDefaultTableShardingStrategy(new StandardShardingStrategyConfiguration("id"."customTableSharding"));
        shardingRuleConfig.getTables().add(ShardingTableRuleConfiguration);

        // Configure the library sorting algorithm
        Properties dbShardingAlgorithmProps = new Properties();
        dbShardingAlgorithmProps.setProperty("strategy"."standard");
        dbShardingAlgorithmProps.setProperty("algorithmClassName"."com.shardingsphere.shardingdb.config.CustomDbSharding");
        shardingRuleConfig.getShardingAlgorithms().put("customDbSharding".new ShardingSphereAlgorithmConfiguration("CLASS_BASED", dbShardingAlgorithmProps));

        // Configure the sub-table algorithm
        Properties tableShardingAlgorithmProps = new Properties();
        tableShardingAlgorithmProps.setProperty("strategy"."standard");
        tableShardingAlgorithmProps.setProperty("algorithmClassName"."com.shardingsphere.shardingdb.config.CustomTableSharding");
        shardingRuleConfig.getShardingAlgorithms().put("customTableSharding".new ShardingSphereAlgorithmConfiguration("CLASS_BASED", tableShardingAlgorithmProps));

        // Enable Sql logging
        final Properties properties = new Properties();
        properties.setProperty("sql-show"."true");

        / / create ShardingSphereDataSource
        return ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfig), properties);
    }

    @Override
    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource")DataSource dataSource) throws Exception {
        return super.sqlSessionFactory(getDataSource());
    }

    @Override
    @Bean("sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory")SqlSessionFactory sqlSessionFactory) {
        return super.sqlSessionTemplate(sqlSessionFactory); }}Copy the code

As you can see from the code, most of it is still easy to directly repair the configuration file later for extension

The code for the custom branch is as follows: basically get the data source name information in ThreadLocal and return it to Shardingsphere so that the corresponding data source can be accessed

This straightforward approach is used for simplicity in the example, but other information can be put in and converted to the corresponding data source as required

public final class CustomDbSharding implements StandardShardingAlgorithm<Integer> {

    @Override
    public void init(a) {}@Override
    public String doSharding(final Collection<String> availableTargetNames, final PreciseShardingValue<Integer> shardingValue) {
        String dbName = ThreadLocalCache.threadLocal.get();
        for (String each : availableTargetNames) {
            if (each.equals(dbName)) {
                returneach; }}return null;
    }

    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final RangeShardingValue<Integer> shardingValue) {
        return availableTargetNames;
    }

    @Override
    public String getType(a) {
        return null; }}Copy the code

In fact, there should be no custom sub-table, but in order to show a complete, so also made a custom sub-table, here is directly return can be

public final class CustomTableSharding implements StandardShardingAlgorithm<Integer> {

    @Override
    public void init(a) {}@Override
    public String doSharding(final Collection<String> availableTargetNames, final PreciseShardingValue<Integer> shardingValue) {
        for (String each : availableTargetNames) {
            return each;
        }
        return null;
    }

    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final RangeShardingValue<Integer> shardingValue) {
        return availableTargetNames;
    }

    @Override
    public String getType(a) {
        return null; }}Copy the code

Entity and Mapper definitions

Simply write:

@Data
@TableName("sharding_table")
public class ShardingTable {
    private Long id;
}

@Data
@TableName("table1")
public class Table1 {
    private Long id;
}

@Data
@TableName("table2")
public class Table2 {

    private Long id;
}

@Repository
public interface ShardingTableMapper extends BaseMapper<ShardingTable> {}@Repository
public interface Table1Mapper extends BaseMapper<Table1> {}@Repository
public interface Table2Mapper extends BaseMapper<Table2> {}Copy the code

The validation test

We’ve written our test class, and we just test it

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ShardingDbTest {

    @Autowired
    private Table1Mapper table1Mapper;
    @Autowired
    private Table2Mapper table2Mapper;
    @Autowired
    private ShardingTableMapper shardingTableMapper;

    @Test
    public void test(a) {
        final List<Table1> l1 = table1Mapper.selectList(null);
        l1.forEach(System.out::println);

        final List<Table2> l2 = table2Mapper.selectList(null);
        l2.forEach(System.out::println);

        ThreadLocalCache.threadLocal.set("db1");
        System.out.println(shardingTableMapper.selectById(1L));

        ThreadLocalCache.threadLocal.set("db0");
        System.out.println(shardingTableMapper.selectById(1L)); }}Copy the code

The results are as follows:

Logic SQL: SELECT id FROM table1 SQLStatement: MySQLSelectStatement(limit=Optional.empty, lock=Optional.empty, window=Optional.empty) Actual SQL: db0 ::: SELECT id FROM table1 Logic SQL: SELECT id FROM table2 SQLStatement: MySQLSelectStatement(limit=Optional.empty, lock=Optional.empty, window=Optional.empty) Actual SQL: db1 ::: SELECT id FROM table2 Logic SQL: SELECT id FROM sharding_table WHERE id=? SQLStatement: MySQLSelectStatement(limit=Optional.empty, lock=Optional.empty, window=Optional.empty) Actual SQL: db1 ::: SELECT id FROM sharding_table WHERE id=? ::: [1] ShardingTable(id=1) Logic SQL: SELECT id FROM sharding_table WHERE id=? SQLStatement: MySQLSelectStatement(limit=Optional.empty, lock=Optional.empty, window=Optional.empty) Actual SQL: db0 ::: SELECT id FROM sharding_table WHERE id=? : : : [1]Copy the code

You can see that with four visits, Actual SQL meets our expectations

conclusion

How to use Shardingsphere JDBC to implement multi-data source access, Shardingsphere JDBC how to implement custom sub-library and sub-table algorithm

Refer to the link

  • Shardingsphere uses the JAVA API
  • Shardingsphere YAML configuration data fragments
  • ThreadLocal usage and principles
  • Shardingsphere algorithm
  • How to configure a custom sharding strategy?