SQLJoin.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. package com.primeton.dsp.datarelease.api.sql;
  2. import com.alibaba.druid.sql.SQLUtils;
  3. import com.google.common.base.Preconditions;
  4. import com.primeton.dsp.datarelease.api.model.Field;
  5. import com.primeton.dsp.datarelease.api.model.SelectField;
  6. import com.primeton.dsp.datarelease.api.model.Table;
  7. import com.primeton.dsp.datarelease.api.model.WhereCause;
  8. import lombok.NonNull;
  9. import net.sf.jsqlparser.JSQLParserException;
  10. import net.sf.jsqlparser.expression.Alias;
  11. import net.sf.jsqlparser.expression.BinaryExpression;
  12. import net.sf.jsqlparser.expression.DateValue;
  13. import net.sf.jsqlparser.expression.DoubleValue;
  14. import net.sf.jsqlparser.expression.Expression;
  15. import net.sf.jsqlparser.expression.LongValue;
  16. import net.sf.jsqlparser.expression.StringValue;
  17. import net.sf.jsqlparser.expression.TimestampValue;
  18. import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
  19. import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
  20. import net.sf.jsqlparser.expression.operators.relational.*;
  21. import net.sf.jsqlparser.parser.CCJSqlParserUtil;
  22. import net.sf.jsqlparser.schema.Column;
  23. import net.sf.jsqlparser.statement.select.*;
  24. import net.sf.jsqlparser.util.SelectUtils;
  25. import org.apache.commons.lang.StringUtils;
  26. import java.util.*;
  27. import java.util.stream.Collectors;
  28. /**
  29. * <pre>
  30. *
  31. * Created by zhaopx.
  32. * User: zhaopx
  33. * Date: 2019/2/21
  34. * Time: 10:29
  35. * Vendor: primeton.com
  36. *
  37. * </pre>
  38. *
  39. * @author zhaopx
  40. */
  41. public abstract class SQLJoin {
  42. /**
  43. * 输出 SQL
  44. *
  45. * @return 返回 sql str
  46. */
  47. public abstract String show(boolean format);
  48. public static class Builder {
  49. /**
  50. * 核心 Select
  51. */
  52. Select select;
  53. /**
  54. * 合并后的 All Fields
  55. */
  56. List<SelectField> fields;
  57. /**
  58. * 内部对表的缓存
  59. */
  60. final Map<String, JoinTable> CACHED_TABLE = new HashMap<>();
  61. /**
  62. * 别名和表的缓存
  63. */
  64. final Map<String, JoinTable> ALIAS_NAME_CACHED_TABLE = new HashMap<>();
  65. /**
  66. * 两个表关联
  67. *
  68. * @param left
  69. * 左表
  70. * @param right
  71. * 右表
  72. * @param type
  73. * 关联方式
  74. */
  75. public Builder(JoinTable left, String leftField, JoinTable right, String rightField, JoinType type) {
  76. // 合并需要查询的列,所有的列,不能重复
  77. List<SelectField> fields = new ArrayList<>();
  78. // 去重复后的字段 ID
  79. Set<String> distictFields = new HashSet<>();
  80. // 检查左表是否有重复的字段
  81. for(SelectField col : left.getTableFields()) {
  82. String tmpField = StringUtils.upperCase(col.getExpression().getColumnName());
  83. if(!distictFields.contains(tmpField)) {
  84. distictFields.add(tmpField);
  85. fields.add(col);
  86. }
  87. }
  88. // 检查右表是否有重复的字段
  89. for(SelectField col : right.getTableFields()) {
  90. String tmpField = StringUtils.upperCase(col.getExpression().getColumnName());
  91. if(!distictFields.contains(tmpField)) {
  92. distictFields.add(tmpField);
  93. fields.add(col);
  94. }
  95. }
  96. ALIAS_NAME_CACHED_TABLE.put(StringUtils.upperCase(left.getAlias()), left);
  97. if(ALIAS_NAME_CACHED_TABLE.get(StringUtils.upperCase(right.getAlias())) != null) {
  98. // 表的别名重名了,已经存在
  99. right.setAlias(right.getAlias()+"1"); // 第一次重名,可以确定的
  100. }
  101. // 如果别名重复,可能修改了别名的
  102. ALIAS_NAME_CACHED_TABLE.put(StringUtils.upperCase(right.getAlias()), right);
  103. // 生成字段别名
  104. Expression[] columns = (Expression[]) fields.toArray(new SelectField[fields.size()]);
  105. Select select = SelectUtils.buildSelectFromTableAndExpressions(
  106. left.getTable(), columns);
  107. Join join = SelectUtils.addJoin(select, right.getTable(), null);
  108. EqualsTo on = new EqualsTo();
  109. on.setLeftExpression(left.getJoinColumn(leftField));
  110. on.setRightExpression(right.getJoinColumn(rightField));
  111. join.setOnExpression(on);
  112. switch (type) {
  113. case INNER_JOIN:
  114. join.setInner(true);
  115. break;
  116. case LEFT_JOIN:
  117. join.setLeft(true);
  118. break;
  119. case RIGHT_JOIN:
  120. join.setRight(true);
  121. break;
  122. default:
  123. join.setFull(true);
  124. }
  125. CACHED_TABLE.put(StringUtils.upperCase(left.getTableName()), left);
  126. CACHED_TABLE.put(StringUtils.upperCase(right.getTableName()), right);
  127. this.select = select;
  128. this.fields = fields;
  129. }
  130. /**
  131. * 以当前关联结果再次关联
  132. * @param leftJoinTable 左边的关联表
  133. * @param leftField 左边的关联字段
  134. * @param rightJoinTable 右边关联表
  135. * @param rightField 右边的关联字段
  136. * @param type 关联方式
  137. */
  138. public Builder join(JoinTable leftJoinTable, String leftField, JoinTable rightJoinTable, String rightField, JoinType type) {
  139. JoinTable table1 = CACHED_TABLE.get(StringUtils.upperCase(leftJoinTable.getTableName()));
  140. if (table1 == null) {
  141. // 关联表没有加入到 cache
  142. table1 = leftJoinTable;
  143. CACHED_TABLE.put(StringUtils.upperCase(leftJoinTable.getTableName()), leftJoinTable);
  144. }
  145. // 检查别名是否重复
  146. if(ALIAS_NAME_CACHED_TABLE.get(StringUtils.upperCase(rightJoinTable.getAlias())) != null) {
  147. // 表的别名重名了,已经存在
  148. rightJoinTable.setAlias(rightJoinTable.getAlias()+ALIAS_NAME_CACHED_TABLE.size());
  149. }
  150. // 如果别名重复,可能修改了别名的
  151. ALIAS_NAME_CACHED_TABLE.put(StringUtils.upperCase(rightJoinTable.getAlias()), rightJoinTable);
  152. try {
  153. // 去重复后的字段 ID
  154. Set<String> distictFields = new HashSet<>();
  155. for (SelectField col : this.fields) {
  156. String tmpField = StringUtils.upperCase(col.getExpression().getColumnName());
  157. distictFields.add(tmpField);
  158. }
  159. // 拿到所有需要查询的字段,可能存在重复的
  160. List<SelectField> addTableFields = rightJoinTable.getTableFields();
  161. List<Column> tableFields = new ArrayList<>(addTableFields.size());
  162. // 检查左表是否有重复的字段
  163. for(SelectField col : addTableFields) {
  164. String tmpField = StringUtils.upperCase(col.getExpression().getColumnName());
  165. // 不存在才放进来
  166. if(!distictFields.contains(tmpField)) {
  167. distictFields.add(tmpField);
  168. fields.add(col);
  169. tableFields.add(col.getExpression());
  170. }
  171. }
  172. SelectItem[] addField = new SelectItem[tableFields.size()];
  173. for (int i = 0; i < tableFields.size(); i++) {
  174. addField[i] = new SelectExpressionItem(
  175. CCJSqlParserUtil.parseExpression(tableFields.get(i)
  176. .getName(true)));
  177. }
  178. SelectBody selectBody = select.getSelectBody();
  179. ((PlainSelect) selectBody).addSelectItems(addField);
  180. } catch (JSQLParserException e) {
  181. throw new IllegalStateException(e);
  182. }
  183. Join join = SelectUtils.addJoin(select, rightJoinTable.getTable(), null);
  184. EqualsTo on2 = new EqualsTo();
  185. on2.setLeftExpression(table1.getJoinColumn(leftField));
  186. on2.setRightExpression(rightJoinTable.getJoinColumn(rightField));
  187. join.setOnExpression(on2);
  188. switch (type) {
  189. case INNER_JOIN:
  190. join.setInner(true);
  191. break;
  192. case LEFT_JOIN:
  193. join.setLeft(true);
  194. break;
  195. case RIGHT_JOIN:
  196. join.setRight(true);
  197. break;
  198. default:
  199. join.setFull(true);
  200. }
  201. CACHED_TABLE.put(StringUtils.upperCase(rightJoinTable.getTableName()), rightJoinTable);
  202. return this;
  203. }
  204. /**
  205. * 是否是已经关联缓存的表
  206. * @param tableName
  207. * @return
  208. */
  209. public boolean isCachedTable(@NonNull String tableName) {
  210. return CACHED_TABLE.get(StringUtils.upperCase(tableName)) != null;
  211. }
  212. /**
  213. * 设置 where 调用条件。调用 where 后就不应该再调用 join 了
  214. * @return
  215. */
  216. public Builder where(WhereCause... wheres) {
  217. if(wheres == null || wheres.length == 0) {
  218. // 没有可加的条件
  219. return this;
  220. }
  221. PlainSelect ps = (PlainSelect)select.getSelectBody();
  222. Expression where = ps.getWhere();
  223. if(where == null && wheres.length == 1) {
  224. // 一个条件,就这样了。
  225. JoinTable table = CACHED_TABLE.get(StringUtils.upperCase(wheres[0].getTableName()));
  226. Expression expr = buildExpression(table, wheres[0]);
  227. ps.setWhere(expr);
  228. } else if(where == null){
  229. // where is null,wheres 第一个不加 and,后续都加 and。
  230. JoinTable table = CACHED_TABLE.get(StringUtils.upperCase(wheres[0].getTableName()));
  231. Expression firstExpr = buildExpression(table, wheres[0]);
  232. WhereCause[] whereCauses1toEnd = new WhereCause[wheres.length - 1];
  233. System.arraycopy(wheres, 1, whereCauses1toEnd, 0, whereCauses1toEnd.length);
  234. ps.setWhere(buildWhereCause(firstExpr, whereCauses1toEnd));
  235. } else {
  236. // where is not null,第一个条件就需要加 and
  237. ps.setWhere(buildWhereCause(where, wheres));
  238. }
  239. return this;
  240. }
  241. /**
  242. * 创建循环的 where 条件
  243. * @param wheres 一个或者多个 where
  244. * @return
  245. */
  246. private Expression buildWhereCause(Expression last, WhereCause... wheres) {
  247. if(wheres.length == 1) {
  248. JoinTable table = CACHED_TABLE.get(StringUtils.upperCase(wheres[0].getTableName()));
  249. Expression expression = buildExpression(table, wheres[0]);
  250. BinaryExpression expr = null;
  251. if("or".equalsIgnoreCase(wheres[0].getCond())){
  252. expr = new OrExpression(last, expression);
  253. } else {
  254. expr = new AndExpression(last, expression);
  255. }
  256. return expr;
  257. }
  258. JoinTable table = CACHED_TABLE.get(StringUtils.upperCase(wheres[0].getTableName()));
  259. Expression addExpr = buildExpression(table, wheres[0]);
  260. BinaryExpression expr = null;
  261. if("or".equalsIgnoreCase(wheres[0].getCond())){
  262. expr = new OrExpression(last, addExpr);
  263. } else {
  264. expr = new AndExpression(last, addExpr);
  265. }
  266. WhereCause[] whereCauses1toEnd = new WhereCause[wheres.length - 1];
  267. System.arraycopy(wheres, 1, whereCauses1toEnd, 0, whereCauses1toEnd.length);
  268. // 递归处理每一个表达式
  269. return buildWhereCause(expr, whereCauses1toEnd);
  270. }
  271. /**
  272. * 根据 where 条件,把前台选择的条件转为 sql 支持的结构。
  273. * @param table 表名称
  274. * @param cause 条件
  275. * @return
  276. */
  277. private Expression buildExpression(JoinTable table, WhereCause cause) {
  278. String[] mutilValue = cause.getValue() != null ? cause.getValue().split(",") : new String[]{};
  279. if (mutilValue.length > 1) {
  280. // 多值的情况 select * from table where id in('a', 'b')
  281. // 多值的必须是 in 或者 notin 的情况,如果不是,强制改变语句为 in 的条件
  282. String opera = cause.getOpera();
  283. if(!"in".equalsIgnoreCase(opera) && !"notin".equalsIgnoreCase(opera)) {
  284. cause.setOpera("in");
  285. }
  286. // 多个值的情况
  287. return buildSingleValue(table, cause);
  288. }
  289. // 单值的条件
  290. return buildSingleValue(table, cause);
  291. }
  292. /**
  293. * 编译单值的条件
  294. * @param table
  295. * @param cause
  296. * @return
  297. */
  298. private Expression buildSingleValue(JoinTable table, WhereCause cause) {
  299. Expression valueExpr = null;
  300. if(StringUtils.isNotBlank(cause.getToTableName()) && StringUtils.isNotBlank(cause.getToFieldName())) {
  301. // 第二个表名称和表字段名都不为 null,则表达式的值为第二个表中的字段
  302. // a.AGE > b.AGE
  303. JoinTable tmpTable = this.CACHED_TABLE.get(StringUtils.upperCase(cause.getToTableName()));
  304. valueExpr = new Column(tmpTable.getTable(), cause.getToFieldName());
  305. } else if("INT".equalsIgnoreCase(cause.getType())) {
  306. // 表达式为常量,但是值为数值类型,SQL 中数值不加引号
  307. valueExpr = new LongValue(cause.getValue());
  308. } else if("DOUBLE".equalsIgnoreCase(cause.getType())) {
  309. // 表达式为常量,但是值为浮点类型,SQL 中数值不加引号
  310. valueExpr = new DoubleValue(cause.getValue());
  311. } else if("DATE".equalsIgnoreCase(cause.getType())) {
  312. // 表达式为常量,但是值为日期类型,SQL 中数值不加引号
  313. // 日期类型为:yyyy-[M]M-[d]d
  314. valueExpr = new DateValue(cause.getValue());
  315. } else if("DATETIME".equalsIgnoreCase(cause.getType())) {
  316. // 表达式为常量,但是值为日期类型,SQL 中数值不加引号
  317. // 日期类型为:yyyy-[M]M-[d]d HH:mm:ss
  318. valueExpr = new TimestampValue(cause.getValue());
  319. } else {
  320. // 表达式值为常量,字符串, NAME = 'X'
  321. valueExpr = new StringValue(cause.getValue());
  322. }
  323. if("=".equals(cause.getOpera())) {
  324. EqualsTo equals = new EqualsTo();
  325. equals.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  326. equals.setRightExpression(valueExpr);
  327. return equals;
  328. } else if(">".equals(cause.getOpera())) {
  329. GreaterThan greaterThan = new GreaterThan();
  330. greaterThan.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  331. greaterThan.setRightExpression(valueExpr);
  332. return greaterThan;
  333. } else if(">=".equals(cause.getOpera())) {
  334. GreaterThanEquals greaterThanEquals = new GreaterThanEquals();
  335. greaterThanEquals.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  336. greaterThanEquals.setRightExpression(valueExpr);
  337. return greaterThanEquals;
  338. } else if("<".equals(cause.getOpera())) {
  339. MinorThan minorThan = new MinorThan();
  340. minorThan.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  341. minorThan.setRightExpression(valueExpr);
  342. return minorThan;
  343. } else if("<=".equals(cause.getOpera())) {
  344. MinorThanEquals minorThanEquals = new MinorThanEquals();
  345. minorThanEquals.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  346. minorThanEquals.setRightExpression(valueExpr);
  347. return minorThanEquals;
  348. } else if("<>".equals(cause.getOpera()) || "!=".equals(cause.getOpera())) {
  349. NotEqualsTo notEqualsTo = new NotEqualsTo();
  350. notEqualsTo.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  351. notEqualsTo.setRightExpression(valueExpr);
  352. return notEqualsTo;
  353. } else if("in".equalsIgnoreCase(cause.getOpera())) {
  354. String[] mutilValue = cause.getValue().split(",");
  355. InExpression inExpression = new InExpression();
  356. inExpression.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  357. MultiExpressionList list = new MultiExpressionList();
  358. List<Expression> inVals = new ArrayList<>();
  359. for (String val : mutilValue) {
  360. inVals.add(new StringValue(val));
  361. }
  362. list.addExpressionList(new ExpressionList(inVals));
  363. inExpression.setRightItemsList(list);
  364. return inExpression;
  365. } else if("notin".equalsIgnoreCase(cause.getOpera())) {
  366. String[] mutilValue = cause.getValue().split(",");
  367. InExpression inExpression = new InExpression();
  368. inExpression.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  369. // not 否定条件
  370. inExpression.setNot(true);
  371. MultiExpressionList list = new MultiExpressionList();
  372. List<Expression> inVals = new ArrayList<>();
  373. for (String val : mutilValue) {
  374. inVals.add(new StringValue(val));
  375. }
  376. list.addExpressionList(new ExpressionList(inVals));
  377. inExpression.setRightItemsList(list);
  378. return inExpression;
  379. } else if("l".equalsIgnoreCase(cause.getOpera())) {
  380. Preconditions.checkNotNull(cause.getValue(), " like value must not be blank");
  381. // 如果自带 %,则说明需要匹配值的 %,用转义
  382. String likeValue = cause.getValue().replaceAll("%", "\\%");
  383. valueExpr = new StringValue("%"+likeValue+"%");
  384. LikeExpression likeExpression = new LikeExpression();
  385. likeExpression.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  386. likeExpression.setRightExpression(valueExpr);
  387. return likeExpression;
  388. }
  389. EqualsTo equals = new EqualsTo();
  390. equals.setLeftExpression(new Column(table.getTable(), cause.getFieldName()));
  391. equals.setRightExpression(valueExpr);
  392. return equals;
  393. }
  394. /**
  395. * 设置 where 调用条件。调用 where 后就不应该再调用 join 了
  396. * @return
  397. */
  398. public Builder where(Collection<WhereCause> wheres) {
  399. where(wheres.toArray(new WhereCause[wheres.size()]));
  400. return this;
  401. }
  402. /**
  403. * 创建 SQL
  404. *
  405. * @return
  406. */
  407. public SQLJoin build() {
  408. return new SQLJoin() {
  409. @Override
  410. public String show(boolean format) {
  411. if(!format) {
  412. return select.toString();
  413. }
  414. String sql = select.toString();
  415. sql = SQLUtils.formatMySql(sql);
  416. return sql;
  417. }
  418. };
  419. }
  420. }
  421. /**
  422. * 数据表
  423. * @author zhaopx
  424. *
  425. */
  426. public static class JoinTable extends Table {
  427. /**
  428. * 内部 sqlparser 的表结构
  429. */
  430. final net.sf.jsqlparser.schema.Table table;
  431. public JoinTable(Table table){
  432. this(table.getTableName(), table.getAlias(), table.getFields());
  433. }
  434. /**
  435. * 构造器
  436. *
  437. * @param tableName
  438. * 表名称
  439. * @param alias
  440. * 关联时的别名
  441. * @param fields
  442. * 关联后 select 的表字段,当前表字段
  443. */
  444. public JoinTable(String tableName, String alias, List<Field> fields) {
  445. super(tableName, alias, fields);
  446. table = new net.sf.jsqlparser.schema.Table(tableName);
  447. table.setAlias(new Alias(alias, false));
  448. }
  449. public net.sf.jsqlparser.schema.Table getTable() {
  450. return table;
  451. }
  452. public List<SelectField> getTableFields() {
  453. if (getFields() == null) {
  454. return Collections.emptyList();
  455. }
  456. List<SelectField> cols = new ArrayList<>(getFields().size());
  457. for (Field col : getFields()) {
  458. Column column = new Column(table, col.getFieldName());
  459. SelectField exprs = new SelectField(column);
  460. exprs.setAlias(new Alias(col.getAlias(), true));
  461. cols.add(exprs);
  462. }
  463. return cols;
  464. }
  465. public Column getJoinColumn(String joinField) {
  466. return new Column(table, joinField);
  467. }
  468. @Override
  469. public void setAlias(String alias) {
  470. super.setAlias(alias);
  471. table.setAlias(new Alias(alias, false));
  472. }
  473. }
  474. /**
  475. * 关联类型
  476. * @author zhaopx
  477. *
  478. */
  479. public static enum JoinType {
  480. /**
  481. * 内连接
  482. */
  483. INNER_JOIN,
  484. /**
  485. * 左外连接
  486. */
  487. LEFT_JOIN,
  488. /**
  489. * 右外连接
  490. */
  491. RIGHT_JOIN,
  492. /**
  493. * 全连接
  494. */
  495. FULL_OUTER_JOIN;
  496. public static JoinType joinType(String joinType) {
  497. if("INNER_JOIN".equalsIgnoreCase(joinType)) {
  498. return INNER_JOIN;
  499. } else if("LEFT_JOIN".equalsIgnoreCase(joinType)) {
  500. return LEFT_JOIN;
  501. } else if("RIGHT_JOIN".equalsIgnoreCase(joinType)) {
  502. return RIGHT_JOIN;
  503. } else if("FULL_OUTER_JOIN".equalsIgnoreCase(joinType)) {
  504. return FULL_OUTER_JOIN;
  505. }
  506. return INNER_JOIN;
  507. }
  508. }
  509. }