阿飞Javaer,转载请注明原创出处,谢谢!
核心源码就在sharding-jdbc-core
模块的com.dangdang.ddframe.rdb.sharding.rewrite
目录下,包含两个文件SQLBuilder和SQLRewriteEngine;测试用例入口为SQLRewriteEngineTest,下面从SQLRewriteEngineTest中debug源码分析sharding-jdbc的重写是如何实现的:
SQLRewriteEngineTest中某个测试用例如下--主要包括表名,offset,limit(rowCount)的重写:
@Test
public void assertRewriteForLimit() {
selectStatement.setLimit(new Limit(true));
// offset的值就是limit offset,rowCount中offset的值
selectStatement.getLimit().setOffset(new LimitValue(2, -1));
// rowCount的值就是limit offset,rowCount中rowCount的值
selectStatement.getLimit().setRowCount(new LimitValue(2, -1));
// TableToken的值表示表名table_x在原始SQL语句的偏移量是17的位置
selectStatement.getSqlTokens().add(new TableToken(17, "table_x"));
// OffsetToken的值表示offset在原始SQL语句的偏移量是33的位置(2就是offset的值)
selectStatement.getSqlTokens().add(new OffsetToken(33, 2));
// RowCountToken的值表示rowCount在原始SQL语句的偏移量是36的位置(2就是rowCount的值)
selectStatement.getSqlTokens().add(new RowCountToken(36, 2));
// selectStatement值模拟过程,实际上是SQL解释过程(SQL解释会单独分析)
SQLRewriteEngine rewriteEngine = new SQLRewriteEngine(shardingRule, "SELECT x.id FROM table_x x LIMIT 2, 2", selectStatement);
// 重写的核心就是这里了:rewriteEngine.rewrite(true)
assertThat(rewriteEngine.rewrite(true).toSQL(tableTokens), is("SELECT x.id FROM table_1 x LIMIT 0, 4"));
}
重写方法核心源码:
从这段源码可知,sql重写主要包括对表名,limit offset, rowNum以及order by的重写(ItemsToken值对select col1, col2 from... 即查询结果列的重写--指那些由于ordre by或者group by需要增加的结果列);
public SQLBuilder rewrite(final boolean isRewriteLimit) {
SQLBuilder result = new SQLBuilder();
if (sqlTokens.isEmpty()) {
result.appendLiterals(originalSQL);
return result;
}
int count = 0;
// 根据Token的beginPosition即出现的位置排序
sortByBeginPosition();
for (SQLToken each : sqlTokens) {
if (0 == count) {
// 第一次处理:截取从原生SQL的开始位置到第一个token起始位置之间的内容,例如"SELECT x.id FROM table_x x LIMIT 2, 2"这条SQL的第一个token是TableToken,即table_x所在位置,所以截取内容为"SELECT x.id FROM "
result.appendLiterals(originalSQL.substring(0, each.getBeginPosition()));
}
if (each instanceof TableToken) {
// 看后面的"表名重写分析"
appendTableToken(result, (TableToken) each, count, sqlTokens);
} else if (each instanceof ItemsToken) {
// ItemsToken是指当逻辑SQL有order by,group by这样的特殊条件时,需要在select的结果列中增加一些结果列,例如执行逻辑SQL:"SELECT o.* FROM t_order o where o.user_id=? order by o.order_id desc limit 2,3",那么还需要增加结果列o.order_id AS ORDER_BY_DERIVED_0
appendItemsToken(result, (ItemsToken) each, count, sqlTokens);
} else if (each instanceof RowCountToken) {
// 看后面的"rowCount重写分析"
appendLimitRowCount(result, (RowCountToken) each, count, sqlTokens, isRewriteLimit);
} else if (each instanceof OffsetToken) {
// 看后面的"offset重写分析"
appendLimitOffsetToken(result, (OffsetToken) each, count, sqlTokens, isRewriteLimit);
} else if (each instanceof OrderByToken) {
appendOrderByToken(result, count, sqlTokens);
}
count++;
}
return result;
}
private void sortByBeginPosition() {
Collections.sort(sqlTokens, new Comparator() {
// 升序排列
@Override
public int compare(final SQLToken o1, final SQLToken o2) {
return o1.getBeginPosition() - o2.getBeginPosition();
}
});
}
表名重写分析
private void appendTableToken(final SQLBuilder sqlBuilder, final TableToken tableToken, final int count, final List sqlTokens) {
String tableName = sqlStatement.getTables().getTableNames().contains(tableToken.getTableName()) ? tableToken.getTableName() : tableToken.getOriginalLiterals();
// append表名特殊处理
sqlBuilder.appendTable(tableName);
int beginPosition = tableToken.getBeginPosition() + tableToken.getOriginalLiterals().length();
appendRest(sqlBuilder, count, sqlTokens, beginPosition);
}
// append表名特殊处理,把TableToken也要添加到SQLBuilder中(List