SQLRouteEngine.java 5.4 KB
Newer Older
T
terrymanu 已提交
1
/*
T
terrymanu 已提交
2 3 4 5 6
 * 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
T
terrymanu 已提交
7
 *
T
terrymanu 已提交
8
 *      http://www.apache.org/licenses/LICENSE-2.0
T
terrymanu 已提交
9
 *
T
terrymanu 已提交
10 11 12 13 14 15 16 17 18 19 20 21
 * 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;

import com.codahale.metrics.Timer.Context;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
T
terrymanu 已提交
22
import com.dangdang.ddframe.rdb.sharding.constants.DatabaseType;
T
terrymanu 已提交
23 24 25 26
import com.dangdang.ddframe.rdb.sharding.exception.SQLParserException;
import com.dangdang.ddframe.rdb.sharding.metrics.MetricsContext;
import com.dangdang.ddframe.rdb.sharding.parser.SQLParserFactory;
import com.dangdang.ddframe.rdb.sharding.parser.result.SQLParsedResult;
27
import com.dangdang.ddframe.rdb.sharding.parser.result.merger.Limit;
T
terrymanu 已提交
28 29 30 31 32 33 34
import com.dangdang.ddframe.rdb.sharding.parser.result.router.ConditionContext;
import com.dangdang.ddframe.rdb.sharding.parser.result.router.Table;
import com.dangdang.ddframe.rdb.sharding.router.binding.BindingTablesRouter;
import com.dangdang.ddframe.rdb.sharding.router.mixed.MixedTablesRouter;
import com.dangdang.ddframe.rdb.sharding.router.single.SingleTableRouter;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
T
fix #53  
terrymanu 已提交
35
import com.google.common.collect.Sets;
T
terrymanu 已提交
36
import lombok.RequiredArgsConstructor;
37
import lombok.extern.slf4j.Slf4j;
T
terrymanu 已提交
38

T
terrymanu 已提交
39
import java.util.Collections;
T
terrymanu 已提交
40 41 42
import java.util.List;
import java.util.Set;

T
terrymanu 已提交
43 44 45
/**
 * SQL路由引擎.
 * 
T
terrymanu 已提交
46 47
 * @author gaohongtao
 * @author zhangiang
T
terrymanu 已提交
48 49
 */
@RequiredArgsConstructor
50
@Slf4j
T
terrymanu 已提交
51 52 53 54 55 56
public final class SQLRouteEngine {
    
    private final ShardingRule shardingRule;
    
    private final DatabaseType databaseType;
    
T
terrymanu 已提交
57 58 59 60 61 62 63 64 65 66 67
    /**
     * SQL路由.
     *
     * @param logicSql 逻辑SQL
     * @return 路由结果
     * @throws SQLParserException SQL解析失败异常
     */
    public SQLRouteResult route(final String logicSql) throws SQLParserException {
        return route(logicSql, Collections.emptyList());
    }
    
G
gaohongtao 已提交
68
    SQLRouteResult route(final String logicSql, final List<Object> parameters) throws SQLParserException {
G
gaohongtao 已提交
69
        return routeSQL(parseSQL(logicSql, parameters), parameters);
T
terrymanu 已提交
70 71
    }
    
72 73 74 75 76 77 78
    /**
     * 预解析SQL路由.
     * 
     * @param logicSql 逻辑SQL
     * @return 预解析SQL路由器
     */
    public PreparedSQLRouter prepareSQL(final String logicSql) {
79
        return new PreparedSQLRouter(logicSql, this, shardingRule);
80 81 82
    }
    
    SQLParsedResult parseSQL(final String logicSql, final List<Object> parameters) {
T
terrymanu 已提交
83
        Context context = MetricsContext.start("Parse SQL");
84
        SQLParsedResult result = SQLParserFactory.create(databaseType, logicSql, parameters, shardingRule).parse();
T
terrymanu 已提交
85 86 87 88
        MetricsContext.stop(context);
        return result;
    }
    
G
gaohongtao 已提交
89
    SQLRouteResult routeSQL(final SQLParsedResult parsedResult, final List<Object> parameters) {
T
terrymanu 已提交
90
        Context context = MetricsContext.start("Route SQL");
91
        SQLRouteResult result = new SQLRouteResult(parsedResult.getRouteContext().getSqlStatementType(), parsedResult.getMergeContext(), parsedResult.getGeneratedKeyContext());
T
terrymanu 已提交
92
        for (ConditionContext each : parsedResult.getConditionContexts()) {
G
gaohongtao 已提交
93 94
            RoutingResult routingResult = routeSQL(each, parsedResult);
            result.getExecutionUnits().addAll(routingResult.getSQLExecutionUnits(parsedResult.getRouteContext().getSqlBuilder()));
T
terrymanu 已提交
95 96
        }
        MetricsContext.stop(context);
G
gaohongtao 已提交
97 98 99 100 101 102 103 104
        Limit limit = result.getMergeContext().getLimit();
        if (null != limit) {
            limit.replaceSQL(parsedResult.getRouteContext().getSqlBuilder(), result.getExecutionUnits().size() > 1);
            limit.replaceParameters(parameters, result.getExecutionUnits().size() > 1);
        }
        log.debug("final route result is {} target", result.getExecutionUnits().size());
        for (SQLExecutionUnit each : result.getExecutionUnits()) {
            log.debug("{}:{} {}", each.getDataSource(), each.getSql(), parameters);
G
gaoht 已提交
105
        }
106
        log.debug("merge context:{}", result.getMergeContext());
T
terrymanu 已提交
107 108 109
        return result;
    }
    
G
gaohongtao 已提交
110 111 112 113 114 115
    private RoutingResult routeSQL(final ConditionContext conditionContext, final SQLParsedResult parsedResult) {
        Set<String> logicTables = Sets.newLinkedHashSet(Collections2.transform(parsedResult.getRouteContext().getTables(), new Function<Table, String>() {
        
            @Override
            public String apply(final Table input) {
                return input.getName();
116
            }
G
gaohongtao 已提交
117 118 119 120 121 122 123 124 125
        }));
        if (1 == logicTables.size()) {
            return new SingleTableRouter(shardingRule, logicTables.iterator().next(), conditionContext, parsedResult.getRouteContext().getSqlStatementType()).route();
        } 
        if (shardingRule.isAllBindingTables(logicTables)) {
            return new BindingTablesRouter(shardingRule, logicTables, conditionContext, parsedResult.getRouteContext().getSqlStatementType()).route();
        } 
        // TODO 可配置是否执行笛卡尔积
        return new MixedTablesRouter(shardingRule, logicTables, conditionContext, parsedResult.getRouteContext().getSqlStatementType()).route();
126
    }
T
terrymanu 已提交
127
}