ShardingJDBC源码阅读(九)结果合并(下)

510 阅读6分钟

前言

本章继续分析ShardingJDBC的核心步骤:结果合并。

一、ResultProcessEngine

ResultProcessEngine结果处理引擎,是个标记接口,继承OrderAware接口(SPI相关)。

public interface ResultProcessEngine<T extends BaseRule> extends OrderAware<Class<T>> {
}

它有两个子接口:ResultMergerEngineResultDecoratorEngine

1、ResultMergerEngine&ResultMerger

ResultMergerEngine负责创建ResultMerger,只有一个实现类ShardingResultMergerEngine

public interface ResultMergerEngine<T extends BaseRule> extends ResultProcessEngine<T> {
    ResultMerger newInstance(DatabaseType databaseType, T rule, ConfigurationProperties properties, SQLStatementContext sqlStatementContext);
}

ResultMerger执行结果归并。

public interface ResultMerger {
    MergedResult merge(List<QueryResult> queryResults, SQLStatementContext sqlStatementContext, SchemaMetaData schemaMetaData) throws SQLException;
}

2、ResultDecoratorEngine&ResultDecorator

ResultDecorator负责创建ResultDecorator,只有一个实现类EncryptResultDecoratorEngine

public interface ResultDecoratorEngine<T extends BaseRule> extends ResultProcessEngine<T> {
    ResultDecorator newInstance(DatabaseType databaseType, SchemaMetaData schemaMetaData, T rule, ConfigurationProperties properties, SQLStatementContext sqlStatementContext);
}

ResultDecorator针对归并结果做装饰,可以针对QueryResult也可以针对MergedResult。

public interface ResultDecorator {
    MergedResult decorate(QueryResult queryResult, SQLStatementContext sqlStatementContext, SchemaMetaData schemaMetaData) throws SQLException;
    MergedResult decorate(MergedResult mergedResult, SQLStatementContext sqlStatementContext, SchemaMetaData schemaMetaData) throws SQLException;
}

二、MergeEntry

MergeEntry是执行结果归并的入口。

1、注册ResultProcessEngine

与DataNodeRouter(路由)、SQLRewriteEntry(重写)一样,归并也是通过外部注入BaseRule与处理类ResultProcessEngine的映射关系。

public final class MergeEntry {
    // BaseRule - ResultProcessEngine
    private final Map<BaseRule, ResultProcessEngine> engines = new LinkedHashMap<>();
    
    public void registerProcessEngine(final BaseRule rule, final ResultProcessEngine processEngine) {
        engines.put(rule, processEngine);
    }
}

注册入口即MergeEngine,先执行registerMergeDecorator方法,使用SPI机制找到所有ResultProcessEngine,向MergeEntry注册BaseRule对应的ResultProcessEngine。

public final class MergeEngine {
    
    private final Collection<BaseRule> rules;
    
    private final MergeEntry merger;
    
    public MergeEngine(final Collection<BaseRule> rules, final ConfigurationProperties properties, final DatabaseType databaseType, final SchemaMetaData metaData) {
        this.rules = rules;
        merger = new MergeEntry(databaseType, metaData, properties);
    }
    public MergedResult merge(final List<QueryResult> queryResults, final SQLStatementContext sqlStatementContext) throws SQLException {
        registerMergeDecorator();
        return merger.process(queryResults, sqlStatementContext);
    }
    
    private void registerMergeDecorator() {
        for (Class<? extends ResultProcessEngine> each : OrderedRegistry.getRegisteredClasses(ResultProcessEngine.class)) {
            ResultProcessEngine processEngine = createProcessEngine(each);
            Class<?> ruleClass = (Class<?>) processEngine.getType();
            rules.stream().filter(rule -> rule.getClass() == ruleClass || rule.getClass().getSuperclass() == ruleClass).collect(Collectors.toList())
                    .forEach(rule -> merger.registerProcessEngine(rule, processEngine));
        }
    }
    
    private ResultProcessEngine createProcessEngine(final Class<? extends ResultProcessEngine> processEngine) {
        return processEngine.newInstance();
    }
}

接下来执行MergeEntry的process方法,执行归并。

2、归并

MergeEntry的process方法是归并的主流程。

public MergedResult process(final List<QueryResult> queryResults, final SQLStatementContext sqlStatementContext) throws SQLException {
  // 执行BaseRule对应的ResultMergerEngine(目前只有ShardingRule对应的ShardingResultMergerEngine)
  // 如果没有BaseRule对应的ResultMergerEngine,这里mergedResult就是空
  Optional<MergedResult> mergedResult = merge(queryResults, sqlStatementContext);
  Optional<MergedResult> result = mergedResult.isPresent()
          // 如果mergedResult不是空(有配置ShardingRuleConfiguration),执行ResultDecoratorEngine装饰MergedResult
          ? Optional.of(decorate(mergedResult.get(), sqlStatementContext))
          // 如果mergedResult是空(比如只配置了EncryptRuleConfiguration),执行ResultDecoratorEngine装饰QueryResult
          : decorate(queryResults.get(0), sqlStatementContext);
  // 如果结果还是空(比如只配了主从),返回TransparentMergedResult委托第一个QueryResult实现MergedResult
  return result.orElseGet(() -> new TransparentMergedResult(queryResults.get(0)));
}

首先找到ResultMergerEngine实例化ResultMerger,执行ResultMerger的merge方法得到MergedResult。

private final DatabaseType databaseType;
private final SchemaMetaData schemaMetaData;
private final ConfigurationProperties properties;
private final Map<BaseRule, ResultProcessEngine> engines = new LinkedHashMap<>();

private Optional<MergedResult> merge(final List<QueryResult> queryResults, final SQLStatementContext sqlStatementContext) throws SQLException {
  for (Entry<BaseRule, ResultProcessEngine> entry : engines.entrySet()) {
      if (entry.getValue() instanceof ResultMergerEngine) {
          // 实例化ResultMerger
          ResultMerger resultMerger = ((ResultMergerEngine) entry.getValue()).newInstance(databaseType, entry.getKey(), properties, sqlStatementContext);
          // merge
          return Optional.of(resultMerger.merge(queryResults, sqlStatementContext, schemaMetaData));
      }
  }
  return Optional.empty();
}

接下来判断merge方法返回结果MergedResult是否为空,如果为空,执行ResultDecorator对QueryResult装饰为MergedResult,如果中间转换为MergedResult成功,还会对MergedResult做二次装饰。

// 对QueryResult做装饰 转换为MergedResult
// 如果中间转换为MergedResult成功,还会对MergedResult做二次装饰
private Optional<MergedResult> decorate(final QueryResult queryResult, final SQLStatementContext sqlStatementContext) throws SQLException {
    MergedResult result = null;
    for (Entry<BaseRule, ResultProcessEngine> entry : engines.entrySet()) {
        if (entry.getValue() instanceof ResultDecoratorEngine) {
            ResultDecorator resultDecorator = ((ResultDecoratorEngine) entry.getValue()).newInstance(databaseType, schemaMetaData, entry.getKey(), properties, sqlStatementContext);
            // 第一次进来时result为null,对入参queryResult装饰
            // 后续进来result不为null,对result装饰
            result = null == result ? resultDecorator.decorate(queryResult, sqlStatementContext, schemaMetaData) : resultDecorator.decorate(result, sqlStatementContext, schemaMetaData);
        }
    }
    return Optional.ofNullable(result);
}

MergedResult如果不为空,执行ResultDecorator对MergedResult做二次装饰。

// 对MergedResult做二次装饰
private MergedResult decorate(final MergedResult mergedResult, final SQLStatementContext sqlStatementContext) throws SQLException {
  MergedResult result = null;
  for (Entry<BaseRule, ResultProcessEngine> entry : engines.entrySet()) {
      if (entry.getValue() instanceof ResultDecoratorEngine) {
          ResultDecorator resultDecorator = ((ResultDecoratorEngine) entry.getValue()).newInstance(databaseType, schemaMetaData, entry.getKey(), properties, sqlStatementContext);
          // 第一次进来时resultnull,对入参mergedResult装饰
          // 后续进来result不为null,对result装饰
          result = null == result ? resultDecorator.decorate(mergedResult, sqlStatementContext, schemaMetaData) : resultDecorator.decorate(result, sqlStatementContext, schemaMetaData);
      }
  }
  return null == result ? mergedResult : result;
}

process的兜底方案是返回一个TransparentMergedResult,用第一个QueryResult,实现所有MergedResult接口方法。

三、ShardingResultMergerEngine

ShardingResultMergerEngine的newInstance方法,根据sql的类型,创建不同的ResultMerger,比如DQL会创建ShardingDQLResultMerger,兜底创建TransparentResultMerger。

public final class ShardingResultMergerEngine implements ResultMergerEngine<ShardingRule> {
    
    @Override
    public ResultMerger newInstance(final DatabaseType databaseType, final ShardingRule shardingRule, final ConfigurationProperties properties, final SQLStatementContext sqlStatementContext) {
        if (sqlStatementContext instanceof SelectStatementContext) {
            return new ShardingDQLResultMerger(databaseType);
        } 
        if (sqlStatementContext.getSqlStatement() instanceof DALStatement) {
            return new ShardingDALResultMerger(shardingRule);
        }
        return new TransparentResultMerger();
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
    
    @Override
    public Class<ShardingRule> getType() {
        return ShardingRule.class;
    }
}

四、ShardingDQLResultMerger

ShardingDQLResultMerger用于处理查询语句。merge方法针对不同的情况new了不同的MergedResult。首先如果结果集个数只有一个,那么使用IteratorStreamMergedResult。接下来创建字段别名与字段下标索引的映射关系。

@RequiredArgsConstructor
public final class ShardingDQLResultMerger implements ResultMerger {
    
    private final DatabaseType databaseType;
    
    @Override
    public MergedResult merge(final List<QueryResult> queryResults, final SQLStatementContext sqlStatementContext, final SchemaMetaData schemaMetaData) throws SQLException {
        // 如果只有一个结果集,返回IteratorStreamMergedResult
        if (1 == queryResults.size()) {
            return new IteratorStreamMergedResult(queryResults);
        }
        // 创建字段别名与字段下标索引的映射关系
        Map<String, Integer> columnLabelIndexMap = getColumnLabelIndexMap(queryResults.get(0));
        SelectStatementContext selectStatementContext = (SelectStatementContext) sqlStatementContext;
        selectStatementContext.setIndexes(columnLabelIndexMap);
        // 创建不同的MergedResult
        MergedResult mergedResult = build(queryResults, selectStatementContext, columnLabelIndexMap, schemaMetaData);
        // 对分页查询的MergedResult做一次装饰
        return decorate(queryResults, selectStatementContext, mergedResult);
    }
}

build方法。如果分组或聚合或distinct,根据isSameGroupByAndOrderByItems,走GroupByStreamMergedResult或GroupByMemoryMergedResult;如果存在排序,走OrderByStreamMergedResult;兜底返回IteratorStreamMergedResult。

private MergedResult build(final List<QueryResult> queryResults, final SelectStatementContext selectStatementContext,
                               final Map<String, Integer> columnLabelIndexMap, final SchemaMetaData schemaMetaData) throws SQLException {
  // 存在 分组或聚合
  if (isNeedProcessGroupBy(selectStatementContext)) {
      return getGroupByMergedResult(queryResults, selectStatementContext, columnLabelIndexMap, schemaMetaData);
  }
  // 存在 distinct
  if (isNeedProcessDistinctRow(selectStatementContext)) {
      setGroupByForDistinctRow(selectStatementContext);
      return getGroupByMergedResult(queryResults, selectStatementContext, columnLabelIndexMap, schemaMetaData);
  }
  // 存在 排序
  if (isNeedProcessOrderBy(selectStatementContext)) {
      return new OrderByStreamMergedResult(queryResults, selectStatementContext, schemaMetaData);
  }
  return new IteratorStreamMergedResult(queryResults);
}

// 如果isSameGroupByAndOrderByItems,走流式归并结果;否则走内存归并结果。
private MergedResult getGroupByMergedResult(final List<QueryResult> queryResults, final SelectStatementContext selectStatementContext,
                                            final Map<String, Integer> columnLabelIndexMap, final SchemaMetaData schemaMetaData) throws SQLException {
    return selectStatementContext.isSameGroupByAndOrderByItems()
            ? new GroupByStreamMergedResult(columnLabelIndexMap, queryResults, selectStatementContext, schemaMetaData)
            : new GroupByMemoryMergedResult(queryResults, selectStatementContext, schemaMetaData);
}

经过build方法首次获取到MergedResult合并结果后,如果存在分页,会根据db类型做一次装饰。

private MergedResult decorate(final List<QueryResult> queryResults, final SelectStatementContext selectStatementContext, final MergedResult mergedResult) throws SQLException {
    PaginationContext paginationContext = selectStatementContext.getPaginationContext();
    if (!paginationContext.isHasPagination() || 1 == queryResults.size()) {
        return mergedResult;
    }
    String trunkDatabaseName = DatabaseTypes.getTrunkDatabaseType(databaseType.getName()).getName();
    if ("MySQL".equals(trunkDatabaseName) || "PostgreSQL".equals(trunkDatabaseName)) {
        return new LimitDecoratorMergedResult(mergedResult, paginationContext);
    }
    if ("Oracle".equals(trunkDatabaseName)) {
        return new RowNumberDecoratorMergedResult(mergedResult, paginationContext);
    }
    if ("SQLServer".equals(trunkDatabaseName)) {
        return new TopAndRowNumberDecoratorMergedResult(mergedResult, paginationContext);
    }
    return mergedResult;
}

五、EncryptResultDecoratorEngine与EncryptDQLResultDecorator

EncryptResultDecoratorEngine是ResultDecoratorEngine的唯一实现类,创建加密结果装饰器。

public final class EncryptResultDecoratorEngine implements ResultDecoratorEngine<EncryptRule> {
    
    @Override
    public ResultDecorator newInstance(final DatabaseType databaseType, final SchemaMetaData schemaMetaData, 
                                       final EncryptRule encryptRule, final ConfigurationProperties properties, final SQLStatementContext sqlStatementContext) {
        if (sqlStatementContext instanceof SelectStatementContext) {
            return new EncryptDQLResultDecorator(
                    new EncryptorMetaData(schemaMetaData, encryptRule, (SelectStatementContext) sqlStatementContext), properties.<Boolean>getValue(ConfigurationPropertyKey.QUERY_WITH_CIPHER_COLUMN));
        } 
        if (sqlStatementContext.getSqlStatement() instanceof DALStatement) {
            return new EncryptDALResultDecorator();
        }
        return new TransparentResultDecorator();
    }
    
    @Override
    public int getOrder() {
        return 20;
    }
    
    @Override
    public Class<EncryptRule> getType() {
        return EncryptRule.class;
    }
}

EncryptDQLResultDecorator两个decorate方法都是创建EncryptMergedResult。

@RequiredArgsConstructor
public final class EncryptDQLResultDecorator implements ResultDecorator {
    
    private final EncryptorMetaData encryptorMetaData;
    
    private final boolean queryWithCipherColumn;
    
    @Override
    public MergedResult decorate(final QueryResult queryResult, final SQLStatementContext sqlStatementContext, final SchemaMetaData schemaMetaData) {
        return new EncryptMergedResult(encryptorMetaData, new TransparentMergedResult(queryResult), queryWithCipherColumn);
    }
    
    @Override
    public MergedResult decorate(final MergedResult mergedResult, final SQLStatementContext sqlStatementContext, final SchemaMetaData schemaMetaData) {
        return new EncryptMergedResult(encryptorMetaData, mergedResult, queryWithCipherColumn);
    }
}

EncryptMergedResult重点是getValue方法。

@RequiredArgsConstructor
public final class EncryptMergedResult implements MergedResult {
    
    private final EncryptorMetaData metaData;
    
    private final MergedResult mergedResult;
    
    private final boolean queryWithCipherColumn;
    @Override
    public Object getValue(final int columnIndex, final Class<?> type) throws SQLException {
        // query.with.cipher.column 是否启用加密字段
        if (!queryWithCipherColumn) {
            return mergedResult.getValue(columnIndex, type);
        }
        // 找到加解密器
        Optional<Encryptor> encryptor = metaData.findEncryptor(columnIndex);
        if (!encryptor.isPresent()) {
            return mergedResult.getValue(columnIndex, type);
        }
        // 密文
        String ciphertext = (String) mergedResult.getValue(columnIndex, String.class);
        // 解密
        return null == ciphertext ? null : encryptor.get().decrypt(ciphertext);
    }
}

总结

  • ResultMergerEngine创建ResultMerger,ResultMerger只有一个merge方法执行结果归并。
  • ResultDecorator创建ResultDecorator,ResultDecorator针对归并结果做装饰,可以针对QueryResult也可以针对MergedResult。
  • MergeEntry的process方法是结果归并的主流程入口。
  • ShardingResultMergerEngine是ResultMergerEngine的唯一实现类,根据sql的类型,创建不同的ResultMerger。
  • ShardingDQLResultMerger处理查询语句结果合并。merge方法针对不同情况创建不同MergedResult。
    • 如果结果集个数只有一个,使用IteratorStreamMergedResult。
    • 如果存在分组、聚合、去重,使用GroupByStreamMergedResult或GroupByMemoryMergedResult。
    • 如果存在排序,使用OrderByStreamMergedResult。
    • 兜底使用IteratorStreamMergedResult。
  • 针对分页查询,ShardingDQLResultMerger在得到MergedResult之后会做一次装饰。MySQL使用LimitDecoratorMergedResult装饰上面得到的MergedResult。
  • EncryptResultDecoratorEngine是ResultDecoratorEngine的唯一实现类,创建加密结果装饰器EncryptDQLResultDecorator。EncryptDQLResultDecorator会对查询结果做解密。