處理Sharding-jdbc不支援sql
阿新 • • 發佈:2018-12-05
在使用分表的專案中,我們可能只是對資料庫中一張或兩張表進行分表分庫處理,其餘表依然按照正常的處理邏輯。若此時所有的表的操作都交給sharding處理的話,其一 是效率會受到很大的影響,其二是sharding jdbc會不支援很多sql的寫法。
在這裡為了繞開sharding jdbc對資料來源的管理,需要多配置一個數據源 dataSourceForSpring,該資料來源不交給sharding-jdbc來管理,而是spring直接管理。
例:有兩張表 t_user和t_goods 需要對t_user分庫分表 t_goods不需要 新建資料庫sharding-jdbc(t_user,t_goods ) 和 sharding-jdbc2(t_user)
1,資料來源配置
application.properties # 資料來源1 spring.shadingjdbc.datasource2.url=jdbc:mysql://localhost:3306/sharding-jdbc2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false spring.shadingjdbc.datasource2.username=root spring.shadingjdbc.datasource2.password=XMKbnAd0k/ItgRzV53cOFto5fe8pzcFRz2D9bN41OBPGM7XF27XQSu99Vi3B6vJHQsngtgBWZiOlKTZUrTtEnw== #主資料來源 spring.shadingjdbc.datasource.url=jdbc:mysql://localhost:3306/sharding-jdbc?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useServerPrepStmts=true&cachePrepStmts=true spring.shadingjdbc.datasource.username=root spring.shadingjdbc.datasource.password=XMKbnAd0k/ItgRzV53cOFto5fe8pzcFRz2D9bN41OBPGM7XF27XQSu99Vi3B6vJHQsngtgBWZiOlKTZUrTtEnw== 裝載資料來源類 @ConfigurationProperties(prefix = SHARDING_JDBC_DATASOURCE) @Data public static class Druid{ private String url; private String username; private String password; private String initialSize; private String minIdle; private String maxActive; private String maxWait; private String timeBetweenEvictionRunsMillis; private String minEvictableIdleTimeMillis; private String validationQuery; private String testWhileIdle; private String testOnBorrow; private String testOnReturn; private String poolPreparedStatements; private String maxPoolPreparedStatementPerConnectionSize; private String connectionProperties; private String filters; private String removeAbandoned; private String removeAbandonedTimeout; private String logAbandoned; public Map<String,String> propertyToMap() throws IllegalAccessException{ Map<String,String> propertyMap = new HashMap<>(1 << 5); Field[] fields= this.getClass().getDeclaredFields(); for (Field field:fields) { field.setAccessible(true); propertyMap.put(field.getName(),(String)field.get(this)); } return propertyMap; } }
2,設定sharding資料來源和spring資料來源
資料來源設定 @Bean(name = "dataSource2") @ConfigurationProperties(prefix = "spring.shadingjdbc.datasource2") public DataSource dataSource2() { return DataSourceBuilder.create().build(); } /** * 資料來源1 將交給sharding 管理 * @param druidConfig * @return */ @Bean(name = "dataSource1") public DataSource dataSource1(DataSourceConfig.Druid druidConfig) { DataSource result = null; try { result = DruidDataSourceFactory.createDataSource(druidConfig.propertyToMap()); }catch (Exception e){ throw new RuntimeException("資料來源獲取失敗"); } return result; } /** * 主資料來源將交給spring 管理 * @param druidConfig * @return */ @Bean(name = "dataSourceForSpring") public DataSource dataSourceForSpring(DataSourceConfig.Druid druidConfig) { DataSource result = null; try { result = DruidDataSourceFactory.createDataSource(druidConfig.propertyToMap()); }catch (Exception e){ throw new RuntimeException("資料來源獲取失敗"); } return result; } @Bean public DataSourceRule dataSourceRule(@Qualifier("dataSource1")DataSource dataSource1, @Qualifier("dataSource2")DataSource dataSource2){ //設定分庫對映 Map<String, DataSource> dataSourceMap = new HashMap<>(); dataSourceMap.put("dataSource1", dataSource1); dataSourceMap.put("dataSource2", dataSource2); return new DataSourceRule(dataSourceMap,"dataSource1"); } @Bean("shardingRule") public ShardingRule shardingRule(DataSourceRule dataSourceRule) throws SQLException { // 按月動態分表 TableRule byMonthTableRule = TableRule.builder("t_user"). tableShardingStrategy(new TableShardingStrategy("txdate", new DynamicSubTableByMouthAlgorithm("t_user_"))). dataSourceRule(dataSourceRule) .dynamic(true).build(); return ShardingRule.builder() .dataSourceRule(dataSourceRule) .tableRules(Arrays.asList(byMonthTableRule)) .databaseShardingStrategy(new DatabaseShardingStrategy("id",new DynamicSubDatabaseByIdAlgorithm())) .build(); } @Bean(name="dynamicDataSource") public DynamicDataSource dataSource(@Qualifier("shardingRule") ShardingRule shardingRule,@Qualifier("dataSourceForSpring") DataSource dataSourceForSpring) throws SQLException { DataSource shardingDataSource = ShardingDataSourceFactory.createDataSource(shardingRule); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.SHARDING_DATASOURCE,shardingDataSource); targetDataSources.put(DataSourceType.SPRING_DATASOURCE,dataSourceForSpring); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources); dataSource.setDefaultTargetDataSource(dataSourceForSpring); return dataSource; } /** * @param dataSource * @return */ @Bean public DataSourceTransactionManager transactitonManager(@Qualifier("dynamicDataSource") DynamicDataSource dataSource){ return new DataSourceTransactionManager(dataSource); } @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource")DynamicDataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*Mapper.xml")); return bean.getObject(); } @Bean(name = "sqlSessionTemplate") @Primary public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); }
3,動態資料來源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
public enum DataSourceType {
SHARDING_DATASOURCE,SPRING_DATASOURCE;
}
public class DatabaseContextHolder {
private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
public static void setDatabaseType(DataSourceType type){
contextHolder.set(type);
}
public static DataSourceType getDatabaseType(){
return contextHolder.get();
}
}
4,操作資料庫
分表service
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public void insert(UserModel userModel){
DatabaseContextHolder.setDatabaseType(DataSourceType.SHARDING_DATASOURCE);
userMapper.save(userModel);
}
public void batchInsert(List<UserModel> userModels){
DatabaseContextHolder.setDatabaseType(DataSourceType.SHARDING_DATASOURCE);
userModels.stream().forEach(userMapper::save);
}
public List<UserModel> selectByPage(Map<String,String> params){
DatabaseContextHolder.setDatabaseType(DataSourceType.SHARDING_DATASOURCE);
return userMapper.selectByPage(params);
}
}
非分表service
@Service
public class GoodsService {
@Resource
private GoodsMapper goodsMapper;
public void insert(GoodsModel userModel){
DatabaseContextHolder.setDatabaseType(DataSourceType.SPRING_DATASOURCE);
goodsMapper.save(userModel);
}
public void batchInsert(List<GoodsModel> goodsModels){
DatabaseContextHolder.setDatabaseType(DataSourceType.SPRING_DATASOURCE);
goodsModels.stream().forEach(goodsMapper::save);
}
public List<GoodsModel> selectByPage(Map<String,String> params){
DatabaseContextHolder.setDatabaseType(DataSourceType.SPRING_DATASOURCE);
return goodsMapper.selectByPage(params);
}
}
5,分庫分表邏輯類
分庫
public class DynamicSubDatabaseByIdAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Integer> {
@Override
public String doEqualSharding(Collection<String> databaseNames, ShardingValue<Integer> shardingValue) {
for (String each:databaseNames ) {
if (each.endsWith((shardingValue.getValue() % 2+1) +"")){
return each;
}
}
throw new IllegalArgumentException();
}
@Override
public Collection<String> doInSharding(Collection<String> databaseNames, ShardingValue<Integer> shardingValue) {
Collection<String> result = new LinkedHashSet<>(databaseNames.size());
for (Integer value : shardingValue.getValues()) {
for (String tableName : databaseNames) {
if (tableName.endsWith((value % 2+1) + "")) {
result.add(tableName);
}
}
}
return result;
}
@Override
public Collection<String> doBetweenSharding(Collection<String> databaseNames, ShardingValue<Integer> shardingValue) {
Collection<String> result = new LinkedHashSet<>(databaseNames.size());
Range<Integer> range = (Range<Integer>) shardingValue.getValueRange();
for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String each : databaseNames) {
if (each.endsWith((i % 2+1) + "")) {
result.add(each);
}
}
}
return result;
}
}
分表
@RequiredArgsConstructor
public class DynamicSubTableByMouthAlgorithm implements SingleKeyTableShardingAlgorithm<Date> {
private final String tablePrefix;
@Override
public String doEqualSharding(final Collection<String> availableTargetNames, final ShardingValue<Date> shardingValue) {
return tablePrefix +getStringDate(shardingValue.getValue());
}
@Override
public Collection<String> doInSharding(final Collection<String> availableTargetNames, final ShardingValue<Date> shardingValue) {
Collection<String> result = new LinkedHashSet<>(shardingValue.getValues().size());
for (Date value : shardingValue.getValues()) {
result.add(tablePrefix + getStringDate(value));
}
return result;
}
@Override
public Collection<String> doBetweenSharding(final Collection<String> availableTargetNames, final ShardingValue<Date> shardingValue) {
Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
Range<Date> range = shardingValue.getValueRange();
return result;
}
public static String getStringDate(Date date) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMM");
String dateString = formatter.format(date);
return dateString;
}
}