/*
* Copyright 1999-2015 dangdang.com.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* </p>
*/
package com.dangdang.ddframe.rdb.sharding.router.single;
import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.rule.DataNode;
import com.dangdang.ddframe.rdb.sharding.api.rule.DataSourceRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.TableRule;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.DatabaseShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.NoneDatabaseShardingAlgorithm;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.NoneTableShardingAlgorithm;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.hint.HintManagerHolder;
import com.dangdang.ddframe.rdb.sharding.hint.ShardingKey;
import com.dangdang.ddframe.rdb.sharding.parser.result.router.Condition;
import com.dangdang.ddframe.rdb.sharding.parser.result.router.ConditionContext;
import com.dangdang.ddframe.rdb.sharding.parser.result.router.SQLStatementType;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* 单逻辑表的库表路由.
*
* @author gaohongtao
* @author zhangliang
*/
@Slf4j
public final class SingleTableRouter {
private final ShardingRule shardingRule;
private final String logicTable;
private final ConditionContext conditionContext;
private final TableRule tableRule;
private final SQLStatementType sqlStatementType;
public SingleTableRouter(final ShardingRule shardingRule, final String logicTable, final ConditionContext conditionContext, final SQLStatementType sqlStatementType) {
this.shardingRule = shardingRule;
this.logicTable = logicTable;
this.conditionContext = conditionContext;
this.sqlStatementType = sqlStatementType;
Optional<TableRule> tableRuleOptional = shardingRule.tryFindTableRule(logicTable);
if (tableRuleOptional.isPresent()) {
tableRule = tableRuleOptional.get();
} else if (shardingRule.getDataSourceRule().getDefaultDataSource().isPresent()) {
tableRule = createTableRuleWithDefaultDataSource(logicTable, shardingRule.getDataSourceRule());
} else {
throw new IllegalArgumentException(String.format("Cannot find table rule and default data source with logic table: '%s'", logicTable));
}
}
private TableRule createTableRuleWithDefaultDataSource(final String logicTable, final DataSourceRule defaultDataSourceRule) {
Map<String, DataSource> defaultDataSourceMap = new HashMap<>(1);
defaultDataSourceMap.put(defaultDataSourceRule.getDefaultDataSourceName(), defaultDataSourceRule.getDefaultDataSource().get());
return TableRule.builder(logicTable)
.dataSourceRule(new DataSourceRule(defaultDataSourceMap))
.databaseShardingStrategy(new DatabaseShardingStrategy("", new NoneDatabaseShardingAlgorithm()))
.tableShardingStrategy(new TableShardingStrategy("", new NoneTableShardingAlgorithm())).build();
}
/**
* 路由.
*
* @return 路由结果
*/
public SingleRoutingResult route() {
Collection<String> routedDataSources = routeDataSources();
Collection<String> routedTables = routeTables(routedDataSources);
return generateRoutingResult(routedDataSources, routedTables);
}
private Collection<String> routeDataSources() {
DatabaseShardingStrategy strategy = shardingRule.getDatabaseShardingStrategy(tableRule);
List<ShardingValue<?>> shardingValues;
if (HintManagerHolder.isUseShardingHint()) {
shardingValues = getDatabaseShardingValuesFromHint(strategy.getShardingColumns());
} else {
shardingValues = getShardingValues(strategy.getShardingColumns());
}
logBeforeRoute("database", logicTable, tableRule.getActualDatasourceNames(), strategy.getShardingColumns(), shardingValues);
Collection<String> result = new HashSet<>(strategy.doStaticSharding(sqlStatementType, tableRule.getActualDatasourceNames(), shardingValues));
logAfterRoute("database", logicTable, result);
Preconditions.checkState(!result.isEmpty(), "no database route info");
return result;
}
private Collection<String> routeTables(final Collection<String> routedDataSources) {
TableShardingStrategy strategy = shardingRule.getTableShardingStrategy(tableRule);
List<ShardingValue<?>> shardingValues;
if (HintManagerHolder.isUseShardingHint()) {
shardingValues = getTableShardingValuesFromHint(strategy.getShardingColumns());
} else {
shardingValues = getShardingValues(strategy.getShardingColumns());
}
logBeforeRoute("table", logicTable, tableRule.getActualTables(), strategy.getShardingColumns(), shardingValues);
Collection<String> result;
if (tableRule.isDynamic()) {
result = new HashSet<>(strategy.doDynamicSharding(shardingValues));
} else {
result = new HashSet<>(strategy.doStaticSharding(sqlStatementType, tableRule.getActualTableNames(routedDataSources), shardingValues));
}
logAfterRoute("table", logicTable, result);
Preconditions.checkState(!result.isEmpty(), "no table route info");
return result;
}
private List<ShardingValue<?>> getDatabaseShardingValuesFromHint(final Collection<String> shardingColumns) {
List<ShardingValue<?>> result = new ArrayList<>(shardingColumns.size());
for (String each : shardingColumns) {
Optional<ShardingValue<?>> shardingValue = HintManagerHolder.getDatabaseShardingValue(new ShardingKey(logicTable, each));
if (shardingValue.isPresent()) {
result.add(shardingValue.get());
}
}
return result;
}
private List<ShardingValue<?>> getTableShardingValuesFromHint(final Collection<String> shardingColumns) {
List<ShardingValue<?>> result = new ArrayList<>(shardingColumns.size());
for (String each : shardingColumns) {
Optional<ShardingValue<?>> shardingValue = HintManagerHolder.getTableShardingValue(new ShardingKey(logicTable, each));
if (shardingValue.isPresent()) {
result.add(shardingValue.get());
}
}
return result;
}
private List<ShardingValue<?>> getShardingValues(final Collection<String> shardingColumns) {
List<ShardingValue<?>> result = new ArrayList<>(shardingColumns.size());
for (String each : shardingColumns) {
Optional<Condition> condition = conditionContext.find(logicTable, each);
if (condition.isPresent()) {
result.add(SingleRouterUtil.convertConditionToShardingValue(condition.get()));
}
}
return result;
}
private void logBeforeRoute(final String type, final String logicTable, final Collection<?> targets, final Collection<String> shardingColumns, final List<ShardingValue<?>> shardingValues) {
log.debug("Before {} sharding {} routes db names: {} sharding columns: {} sharding values: {}", type, logicTable, targets, shardingColumns, shardingValues);
}
private void logAfterRoute(final String type, final String logicTable, final Collection<String> shardingResults) {
log.debug("After {} sharding {} result: {}", type, logicTable, shardingResults);
}
private SingleRoutingResult generateRoutingResult(final Collection<String> routedDataSources, final Collection<String> routedTables) {
SingleRoutingResult result = new SingleRoutingResult();
for (DataNode each : tableRule.getActualDataNodes(routedDataSources, routedTables)) {
result.put(each.getDataSourceName(), new SingleRoutingTableFactor(logicTable, each.getTableName()));
}
return result;
}
}