架构图
借助SpringBoot能够快速实现分库分表。其核心模块有3个,配置中心、mybatis插件、分库分表算法
配置中心
package com.ratel.exception.config;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
public class MultiDataSourceConfig implements EnvironmentAware {
private Map<Object, Object> targetDatasource = new ConcurrentHashMap<>();
private Object defaultTargetDatasource = null;
@Bean(name = "*")
public TableSelectorPlugin plugin() {
return new TableSelectorPlugin();
}
@Bean(name = "*")
public DataSource dataSourceV2() {
MultiDatasource multiDatasource1 = new MultiDatasource();
multiDatasource1.setTargetDataSources(targetDatasource);
multiDatasource1.setDefaultTargetDataSource(defaultTargetDatasource);
return multiDatasource1;
}
@Bean(name = "*")
public SqlSessionFactoryBean sqlSessionFactory(@Qualifier(value = "*") DataSource dataSource, @Qualifier(value = "*") TableSelectorPlugin tableSelectorPlugin, ApplicationContext applicationContext) throws IOException {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setVfs(SpringBootVFS.class);
factoryBean.setPlugins(tableSelectorPlugin);
return factoryBean;
}
@Bean(name = "*")
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("*");
mapperScannerConfigurer.setBasePackage("*");
mapperScannerConfigurer.setAnnotationClass(Mapper.class);
return mapperScannerConfigurer;
}
@Override
public void setEnvironment(Environment environment) {
String prefix = "router.jdbc.datasource.";
String dataSources = environment.getProperty(prefix + "list");
String[] split = dataSources.split(",");
for (String item : split) {
HikariConfig config = new HikariConfig(* + item + ".properties");
this.targetDatasource.put(item, new HikariDataSource(config));
if (item.equals("default")) {
this.defaultTargetDatasource = new HikariDataSource(config);
}
}
System.out.println("222"+targetDatasource.keySet());
}
}
分表插件
public Object intercept(Invocation invocation) throws Throwable {
Object paramObj = invocation.getArgs()[1];
if (!(paramObj instanceof Map)) {
return invocation.proceed();
} else {
Map<String, Object> params = (Map)paramObj;
String tableName = TABLE_NAMES.get();
if (!StringUtils.isEmpty(tableName) && !params.containsKey(TABLE_ARG_NAME)) {
params.put(TABLE_ARG_NAME, tableName);
}
return invocation.proceed();
}
}
分表算法
package com.ratel.exception.config;
import org.springframework.stereotype.Component;
@Component
public class DsNameDeciderImp implements DsNameDecider{
@Override
public DsName decideDsName(String dbName, int dbCount, long dbShardedValue, String tableName, int tableCount, long tableShardedValue) {
return new DsName(this.hashDbName(dbName, dbCount, dbShardedValue), this.hashTableName(tableName, tableCount, tableShardedValue));
}
@Override
public Integer hashDB(String dbName, int dbCount, long shardedValue) {
return null;
}
@Override
public int hashTable(String tableName, int tableCount, long shardedValue) {
return 0;
}
public String hashDbName(String dbName, int dbCount, long shardedValue) {
return dbCount == 1 ? dbName : dbName + this.hashDB(dbName, dbCount, shardedValue);
}
public String hashTableName(String tableName, int tableCount, long shardedValue) {
return tableCount == 1 ? tableName : tableName + this.hashTable(tableName, tableCount, shardedValue);
}
}
以上便是分库分表的核心代码。只需要将数据源继承AbstractRoutingDataSource抽象类。便可以轻松实现分库分表的功能了