001/* 002 * Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.dialect.impl; 017 018import com.mybatisflex.core.dialect.IDialect; 019import com.mybatisflex.core.dialect.KeywordWrap; 020import com.mybatisflex.core.dialect.LimitOffsetProcessor; 021import com.mybatisflex.core.dialect.OperateType; 022import com.mybatisflex.core.exception.FlexAssert; 023import com.mybatisflex.core.exception.FlexExceptions; 024import com.mybatisflex.core.exception.locale.LocalizedFormats; 025import com.mybatisflex.core.logicdelete.LogicDeleteManager; 026import com.mybatisflex.core.query.CPI; 027import com.mybatisflex.core.query.Join; 028import com.mybatisflex.core.query.QueryColumn; 029import com.mybatisflex.core.query.QueryCondition; 030import com.mybatisflex.core.query.QueryOrderBy; 031import com.mybatisflex.core.query.QueryTable; 032import com.mybatisflex.core.query.QueryWrapper; 033import com.mybatisflex.core.query.UnionWrapper; 034import com.mybatisflex.core.query.With; 035import com.mybatisflex.core.row.Row; 036import com.mybatisflex.core.row.RowCPI; 037import com.mybatisflex.core.table.TableInfo; 038import com.mybatisflex.core.update.RawValue; 039import com.mybatisflex.core.util.ArrayUtil; 040import com.mybatisflex.core.util.CollectionUtil; 041import com.mybatisflex.core.util.SqlUtil; 042import com.mybatisflex.core.util.StringUtil; 043 044import java.util.*; 045import java.util.function.Function; 046import java.util.stream.Collectors; 047import java.util.stream.IntStream; 048 049import static com.mybatisflex.core.constant.SqlConsts.AND; 050import static com.mybatisflex.core.constant.SqlConsts.ASTERISK; 051import static com.mybatisflex.core.constant.SqlConsts.BLANK; 052import static com.mybatisflex.core.constant.SqlConsts.BRACKET_LEFT; 053import static com.mybatisflex.core.constant.SqlConsts.BRACKET_RIGHT; 054import static com.mybatisflex.core.constant.SqlConsts.DELETE; 055import static com.mybatisflex.core.constant.SqlConsts.DELETE_FROM; 056import static com.mybatisflex.core.constant.SqlConsts.DELIMITER; 057import static com.mybatisflex.core.constant.SqlConsts.DUAL; 058import static com.mybatisflex.core.constant.SqlConsts.EMPTY; 059import static com.mybatisflex.core.constant.SqlConsts.EQUALS; 060import static com.mybatisflex.core.constant.SqlConsts.EQUALS_PLACEHOLDER; 061import static com.mybatisflex.core.constant.SqlConsts.FROM; 062import static com.mybatisflex.core.constant.SqlConsts.GROUP_BY; 063import static com.mybatisflex.core.constant.SqlConsts.HAVING; 064import static com.mybatisflex.core.constant.SqlConsts.HINT_END; 065import static com.mybatisflex.core.constant.SqlConsts.HINT_START; 066import static com.mybatisflex.core.constant.SqlConsts.INSERT_INTO; 067import static com.mybatisflex.core.constant.SqlConsts.OR; 068import static com.mybatisflex.core.constant.SqlConsts.ORDER_BY; 069import static com.mybatisflex.core.constant.SqlConsts.PLACEHOLDER; 070import static com.mybatisflex.core.constant.SqlConsts.REFERENCE; 071import static com.mybatisflex.core.constant.SqlConsts.SELECT; 072import static com.mybatisflex.core.constant.SqlConsts.SELECT_ALL_FROM; 073import static com.mybatisflex.core.constant.SqlConsts.SEMICOLON; 074import static com.mybatisflex.core.constant.SqlConsts.SET; 075import static com.mybatisflex.core.constant.SqlConsts.UPDATE; 076import static com.mybatisflex.core.constant.SqlConsts.VALUES; 077import static com.mybatisflex.core.constant.SqlConsts.WHERE; 078 079/** 080 * 通用的方言设计,其他方言可以继承于当前 CommonsDialectImpl 081 * 创建或获取方言请参考 {@link com.mybatisflex.core.dialect.DialectFactory} 082 */ 083public class CommonsDialectImpl implements IDialect { 084 085 protected KeywordWrap keywordWrap = KeywordWrap.BACK_QUOTE; 086 private LimitOffsetProcessor limitOffsetProcessor = LimitOffsetProcessor.MYSQL; 087 088 public CommonsDialectImpl() { 089 } 090 091 public CommonsDialectImpl(LimitOffsetProcessor limitOffsetProcessor) { 092 this.limitOffsetProcessor = limitOffsetProcessor; 093 } 094 095 public CommonsDialectImpl(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) { 096 this.keywordWrap = keywordWrap; 097 this.limitOffsetProcessor = limitOffsetProcessor; 098 } 099 100 @Override 101 public String wrap(String keyword) { 102 return ASTERISK.equals(keyword) || DUAL.equalsIgnoreCase(StringUtil.tryTrim(keyword)) ? 103 keyword : keywordWrap.wrap(keyword); 104 } 105 106 @Override 107 public String wrapColumnAlias(String keyword) { 108// return ASTERISK.equals(keyword) ? keyword : keywordWrap.getPrefix() + keyword + keywordWrap.getSuffix(); 109 return ASTERISK.equals(keyword) ? keyword : keywordWrap.wrap(keyword); 110 } 111 112 @Override 113 public String forHint(String hintString) { 114 return StringUtil.hasText(hintString) ? HINT_START + hintString + HINT_END : EMPTY; 115 } 116 117 @Override 118 public String forInsertRow(String schema, String tableName, Row row) { 119 StringBuilder fields = new StringBuilder(); 120 StringBuilder paramsOrPlaceholder = new StringBuilder(); 121 122 // 插入数据时,可能包含主键 123 Set<String> modifyAttrs = RowCPI.getInsertAttrs(row); 124 int index = 0; 125 for (String attr : modifyAttrs) { 126 fields.append(wrap(attr)); 127 128 Object value = row.get(attr); 129 if (value instanceof RawValue) { 130 paramsOrPlaceholder.append(((RawValue) value).toSql(this)); 131 } else { 132 paramsOrPlaceholder.append(PLACEHOLDER); 133 } 134 if (index != modifyAttrs.size() - 1) { 135 fields.append(DELIMITER); 136 paramsOrPlaceholder.append(DELIMITER); 137 } 138 index++; 139 } 140 141 String table = getRealTable(tableName, OperateType.INSERT); 142 StringBuilder sql = new StringBuilder(); 143 sql.append(INSERT_INTO); 144 if (StringUtil.hasText(schema)) { 145 sql.append(wrap(getRealSchema(schema, table, OperateType.INSERT))).append(REFERENCE); 146 } 147 sql.append(wrap(table)); 148 sql.append(BRACKET_LEFT).append(fields).append(BRACKET_RIGHT); 149 sql.append(VALUES).append(BRACKET_LEFT).append(paramsOrPlaceholder).append(BRACKET_RIGHT); 150 return sql.toString(); 151 } 152 153 154 @Override 155 public String forInsertBatchWithFirstRowColumns(String schema, String tableName, List<Row> rows) { 156 StringBuilder fields = new StringBuilder(); 157 StringBuilder questions = new StringBuilder(); 158 159 Row firstRow = rows.get(0); 160 Set<String> attrs = RowCPI.getInsertAttrs(firstRow); 161 int index = 0; 162 for (String column : attrs) { 163 fields.append(wrap(column)); 164 if (index != attrs.size() - 1) { 165 fields.append(DELIMITER); 166 } 167 index++; 168 } 169 170 for (int i = 0; i < rows.size(); i++) { 171 questions.append(SqlUtil.buildSqlParamPlaceholder(attrs.size())); 172 if (i != rows.size() - 1) { 173 questions.append(DELIMITER); 174 } 175 } 176 177 String table = getRealTable(tableName, OperateType.INSERT); 178 StringBuilder sql = new StringBuilder(); 179 sql.append(INSERT_INTO); 180 if (StringUtil.hasText(schema)) { 181 sql.append(wrap(getRealSchema(schema, table, OperateType.INSERT))).append(REFERENCE); 182 } 183 sql.append(wrap(table)); 184 sql.append(BLANK).append(BRACKET_LEFT) 185 .append(fields) 186 .append(BRACKET_RIGHT).append(BLANK); 187 sql.append(VALUES).append(questions); 188 return sql.toString(); 189 } 190 191 192 @Override 193 public String forDeleteById(String schema, String tableName, String[] primaryKeys) { 194 assertPrimaryKeysNotEmpty(primaryKeys); 195 String table = getRealTable(tableName, OperateType.DELETE); 196 StringBuilder sql = new StringBuilder(); 197 sql.append(DELETE_FROM); 198 if (StringUtil.hasText(schema)) { 199 sql.append(wrap(getRealSchema(schema, table, OperateType.DELETE))).append(REFERENCE); 200 } 201 sql.append(wrap(table)); 202 sql.append(WHERE); 203 for (int i = 0; i < primaryKeys.length; i++) { 204 if (i > 0) { 205 sql.append(AND); 206 } 207 sql.append(wrap(primaryKeys[i])).append(EQUALS_PLACEHOLDER); 208 } 209 prepareAuth(schema, table, sql, OperateType.DELETE); 210 return sql.toString(); 211 } 212 213 214 @Override 215 public String forDeleteBatchByIds(String schema, String tableName, String[] primaryKeys, Object[] ids) { 216 assertPrimaryKeysNotEmpty(primaryKeys); 217 String table = getRealTable(tableName, OperateType.DELETE); 218 StringBuilder sql = new StringBuilder(); 219 sql.append(DELETE_FROM); 220 if (StringUtil.hasText(schema)) { 221 sql.append(wrap(getRealSchema(schema, table, OperateType.DELETE))).append(REFERENCE); 222 } 223 224 sql.append(wrap(table)); 225 sql.append(WHERE); 226 227 // 多主键的场景 228 if (primaryKeys.length > 1) { 229 for (int i = 0; i < ids.length / primaryKeys.length; i++) { 230 if (i > 0) { 231 sql.append(OR); 232 } 233 sql.append(BRACKET_LEFT); 234 for (int j = 0; j < primaryKeys.length; j++) { 235 if (j > 0) { 236 sql.append(AND); 237 } 238 sql.append(wrap(primaryKeys[j])).append(EQUALS_PLACEHOLDER); 239 } 240 sql.append(BRACKET_RIGHT); 241 } 242 } 243 // 单主键 244 else { 245 for (int i = 0; i < ids.length; i++) { 246 if (i > 0) { 247 sql.append(OR); 248 } 249 sql.append(wrap(primaryKeys[0])).append(EQUALS_PLACEHOLDER); 250 } 251 } 252 prepareAuth(schema, table, sql, OperateType.DELETE); 253 return sql.toString(); 254 } 255 256 @Override 257 public String forDeleteByQuery(QueryWrapper queryWrapper) { 258 prepareAuth(queryWrapper, OperateType.DELETE); 259 return buildDeleteSql(queryWrapper); 260 } 261 262 @Override 263 public String forUpdateById(String schema, String tableName, Row row) { 264 String table = getRealTable(tableName, OperateType.UPDATE); 265 StringBuilder sql = new StringBuilder(); 266 Set<String> modifyAttrs = RowCPI.getModifyAttrs(row); 267 Map<String, RawValue> rawValueMap = RowCPI.getRawValueMap(row); 268 String[] primaryKeys = RowCPI.obtainsPrimaryKeyStrings(row); 269 assertPrimaryKeysNotEmpty(primaryKeys); 270 271 sql.append(UPDATE); 272 if (StringUtil.hasText(schema)) { 273 sql.append(wrap(getRealSchema(schema, table, OperateType.UPDATE))).append(REFERENCE); 274 } 275 276 sql.append(wrap(table)).append(SET); 277 int index = 0; 278 for (Map.Entry<String, Object> e : row.entrySet()) { 279 String colName = e.getKey(); 280 if (modifyAttrs.contains(colName) && !ArrayUtil.contains(primaryKeys, colName)) { 281 if (index > 0) { 282 sql.append(DELIMITER); 283 } 284 sql.append(wrap(colName)); 285 286 if (rawValueMap.containsKey(colName)) { 287 sql.append(EQUALS).append(rawValueMap.get(colName).toSql(this)); 288 } else { 289 sql.append(EQUALS_PLACEHOLDER); 290 } 291 292 index++; 293 } 294 } 295 sql.append(WHERE); 296 for (int i = 0; i < primaryKeys.length; i++) { 297 if (i > 0) { 298 sql.append(AND); 299 } 300 sql.append(wrap(primaryKeys[i])).append(EQUALS_PLACEHOLDER); 301 } 302 prepareAuth(schema, table, sql, OperateType.UPDATE); 303 return sql.toString(); 304 } 305 306 @Override 307 public String forUpdateByQuery(QueryWrapper queryWrapper, Row row) { 308 prepareAuth(queryWrapper, OperateType.UPDATE); 309 StringBuilder sqlBuilder = new StringBuilder(); 310 311 Set<String> modifyAttrs = RowCPI.getModifyAttrs(row); 312 Map<String, RawValue> rawValueMap = RowCPI.getRawValueMap(row); 313 314 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 315 if (queryTables == null || queryTables.size() != 1) { 316 throw FlexExceptions.wrap(LocalizedFormats.UPDATE_ONLY_SUPPORT_1_TABLE); 317 } 318 319 // fix: support schema 320 QueryTable queryTable = queryTables.get(0); 321 sqlBuilder.append(UPDATE).append(queryTable.toSql(this, OperateType.UPDATE)).append(SET); 322 int index = 0; 323 for (String modifyAttr : modifyAttrs) { 324 if (index > 0) { 325 sqlBuilder.append(DELIMITER); 326 } 327 328 sqlBuilder.append(wrap(modifyAttr)); 329 330 if (rawValueMap.containsKey(modifyAttr)) { 331 sqlBuilder.append(EQUALS).append(rawValueMap.get(modifyAttr).toSql(this)); 332 } else { 333 sqlBuilder.append(EQUALS_PLACEHOLDER); 334 } 335 336 index++; 337 } 338 339 buildJoinSql(sqlBuilder, queryWrapper, queryTables, OperateType.UPDATE); 340 buildWhereSql(sqlBuilder, queryWrapper, queryTables, false); 341 buildGroupBySql(sqlBuilder, queryWrapper, queryTables); 342 buildHavingSql(sqlBuilder, queryWrapper, queryTables); 343 344 // ignore orderBy and limit 345 buildOrderBySql(sqlBuilder, queryWrapper, queryTables); 346 347 Long limitRows = CPI.getLimitRows(queryWrapper); 348 Long limitOffset = CPI.getLimitOffset(queryWrapper); 349 if (limitRows != null || limitOffset != null) { 350 sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset); 351 } 352 353 return sqlBuilder.toString(); 354 } 355 356 @Override 357 public String forUpdateBatchById(String schema, String tableName, List<Row> rows) { 358 if (rows.size() == 1) { 359 return forUpdateById(schema, tableName, rows.get(0)); 360 } 361 StringBuilder sql = new StringBuilder(); 362 for (Row row : rows) { 363 sql.append(forUpdateById(schema, tableName, row)).append(SEMICOLON).append(BLANK); 364 } 365 return sql.toString(); 366 } 367 368 369 @Override 370 public String forSelectOneById(String schema, String tableName, String[] primaryKeys, Object[] primaryValues) { 371 assertPrimaryKeysNotEmpty(primaryKeys); 372 String table = getRealTable(tableName, OperateType.SELECT); 373 StringBuilder sql = new StringBuilder(SELECT_ALL_FROM); 374 if (StringUtil.hasText(schema)) { 375 sql.append(wrap(getRealSchema(schema, table, OperateType.SELECT))).append(REFERENCE); 376 } 377 sql.append(wrap(table)).append(WHERE); 378 for (int i = 0; i < primaryKeys.length; i++) { 379 if (i > 0) { 380 sql.append(AND); 381 } 382 sql.append(wrap(primaryKeys[i])).append(EQUALS_PLACEHOLDER); 383 } 384 prepareAuth(schema, table, sql, OperateType.SELECT); 385 return sql.toString(); 386 } 387 388 @Override 389 public String forSelectByQuery(QueryWrapper queryWrapper) { 390 prepareAuth(queryWrapper, OperateType.SELECT); 391 return buildSelectSql(queryWrapper); 392 } 393 394 395 ////////////build query sql/////// 396 @Override 397 public String buildSelectSql(QueryWrapper queryWrapper) { 398 return buildSelectSql(queryWrapper, Collections.emptyList()); 399 } 400 401 @Override 402 public String buildSelectSql(QueryWrapper queryWrapper, List<QueryTable> contextTables) { 403 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 404 405 List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper); 406 List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables); 407 allTables = CollectionUtil.merge(allTables, contextTables); 408 409 List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper); 410 411 // 多个表,非 SELECT * 时,需要处理重名字段 412 if (allTables.size() > 1 && selectColumns != null && selectColumns.size() > 1) { 413 IntStream.range(0, selectColumns.size()) 414 .boxed() 415 // 生成 索引-字段值 对应关系 416 .collect(Collectors.toMap(Function.identity(), selectColumns::get)) 417 .entrySet() 418 .stream() 419 // 需要处理别名的情况 420 .filter(e -> StringUtil.hasText(e.getValue().getName())) 421 .filter(e -> StringUtil.noText(e.getValue().getAlias())) 422 .filter(e -> !"*".equals(e.getValue().getName())) 423 // 将相同字段对象放在一个集合里 424 .collect(Collectors.groupingBy(e -> e.getValue().getName(), 425 Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))) 426 .values() 427 .stream() 428 // 过滤出来重名的字段 429 .filter(e -> e.size() > 1) 430 // 合并所有需要加别名的字段 431 .flatMap(Collection::stream) 432 // 过滤出来可以添加别名的列 433 .filter(e -> e.getValue().getTable() != null) 434 .filter(e -> StringUtil.hasText(e.getValue().getTable().getName())) 435 // 添加别名并放回原集合索引位置 436 .forEach(e -> selectColumns.set(e.getKey(), 437 e.getValue().as(e.getValue().getTable().getName() + "$" + e.getValue().getName()))); 438 } 439 440 StringBuilder sqlBuilder = new StringBuilder(); 441 With with = CPI.getWith(queryWrapper); 442 if (with != null) { 443 sqlBuilder.append(with.toSql(this)); 444 } 445 446 buildSelectColumnSql(sqlBuilder, allTables, selectColumns, CPI.getHint(queryWrapper)); 447 448 449 if(CollectionUtil.isNotEmpty(queryTables)) { 450 sqlBuilder.append(FROM).append(StringUtil.join(DELIMITER, queryTables, queryTable -> queryTable.toSql(this, OperateType.SELECT))); 451 } 452 453 buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.SELECT); 454 buildWhereSql(sqlBuilder, queryWrapper, allTables, true); 455 buildGroupBySql(sqlBuilder, queryWrapper, allTables); 456 buildHavingSql(sqlBuilder, queryWrapper, allTables); 457 buildOrderBySql(sqlBuilder, queryWrapper, allTables); 458 459 List<UnionWrapper> unions = CPI.getUnions(queryWrapper); 460 if (CollectionUtil.isNotEmpty(unions)) { 461 sqlBuilder.insert(0, BRACKET_LEFT).append(BRACKET_RIGHT); 462 for (UnionWrapper unionWrapper : unions) { 463 unionWrapper.buildSql(sqlBuilder, this); 464 } 465 } 466 467 Long limitRows = CPI.getLimitRows(queryWrapper); 468 Long limitOffset = CPI.getLimitOffset(queryWrapper); 469 if (limitRows != null || limitOffset != null) { 470 sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset); 471 } 472 473 List<String> endFragments = CPI.getEndFragments(queryWrapper); 474 if (CollectionUtil.isNotEmpty(endFragments)) { 475 for (String endFragment : endFragments) { 476 sqlBuilder.append(BLANK).append(endFragment); 477 } 478 } 479 480 return sqlBuilder.toString(); 481 } 482 483 @Override 484 public String buildNoSelectSql(QueryWrapper queryWrapper) { 485 StringBuilder sqlBuilder = new StringBuilder(); 486 487 buildJoinSql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST, OperateType.SELECT); 488 buildWhereSql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST, true); 489 buildGroupBySql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST); 490 buildHavingSql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST); 491 buildOrderBySql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST); 492 493 List<UnionWrapper> unions = CPI.getUnions(queryWrapper); 494 if (CollectionUtil.isNotEmpty(unions)) { 495 if (sqlBuilder.length() > 0) { 496 sqlBuilder.insert(0, BRACKET_LEFT).append(BRACKET_RIGHT); 497 } 498 for (UnionWrapper unionWrapper : unions) { 499 unionWrapper.buildSql(sqlBuilder, this); 500 } 501 } 502 503 Long limitRows = CPI.getLimitRows(queryWrapper); 504 Long limitOffset = CPI.getLimitOffset(queryWrapper); 505 if (limitRows != null || limitOffset != null) { 506 sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset); 507 } 508 509 List<String> endFragments = CPI.getEndFragments(queryWrapper); 510 if (CollectionUtil.isNotEmpty(endFragments)) { 511 for (String endFragment : endFragments) { 512 sqlBuilder.append(BLANK).append(endFragment); 513 } 514 } 515 516 return sqlBuilder.toString(); 517 } 518 519 private void buildSelectColumnSql(StringBuilder sqlBuilder, List<QueryTable> queryTables, List<QueryColumn> selectColumns, String hint) { 520 sqlBuilder.append(SELECT); 521 sqlBuilder.append(forHint(hint)); 522 if (selectColumns == null || selectColumns.isEmpty()) { 523 sqlBuilder.append(ASTERISK); 524 } else { 525 int index = 0; 526 for (QueryColumn selectColumn : selectColumns) { 527 String selectColumnSql = CPI.toSelectSql(selectColumn, queryTables, this); 528 sqlBuilder.append(selectColumnSql); 529 if (index != selectColumns.size() - 1) { 530 sqlBuilder.append(DELIMITER); 531 } 532 index++; 533 } 534 } 535 } 536 537 538 @Override 539 public String buildDeleteSql(QueryWrapper queryWrapper) { 540 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 541 List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper); 542 List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables); 543 544 // ignore selectColumns 545 StringBuilder sqlBuilder = new StringBuilder(DELETE); 546 String hint = CPI.getHint(queryWrapper); 547 if (StringUtil.hasText(hint)) { 548 sqlBuilder.append(BLANK).append(hint).deleteCharAt(sqlBuilder.length() - 1); 549 } 550 551 // delete with join 552 if (joinTables != null && !joinTables.isEmpty()) { 553 if (queryTables == null || queryTables.isEmpty()) { 554 throw new IllegalArgumentException("Delete with join sql must designate the from table."); 555 } else if (queryTables.size() != 1) { 556 throw new IllegalArgumentException("Delete with join sql must has 1 table only. but current has " + queryTables.size()); 557 } 558 QueryTable queryTable = queryTables.get(0); 559 String table = getRealTable(queryTable.getName(), OperateType.DELETE); 560 if (StringUtil.hasText(queryTable.getSchema())) { 561 sqlBuilder.append(wrap(getRealSchema(queryTable.getSchema(), table, OperateType.DELETE))).append(REFERENCE); 562 } 563 sqlBuilder.append(BLANK).append(wrap(getRealTable(table, OperateType.DELETE))); 564 } 565 566 567 sqlBuilder.append(FROM).append(StringUtil.join(DELIMITER, queryTables, queryTable -> queryTable.toSql(this, OperateType.DELETE))); 568 569 buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.DELETE); 570 buildWhereSql(sqlBuilder, queryWrapper, allTables, false); 571 buildGroupBySql(sqlBuilder, queryWrapper, allTables); 572 buildHavingSql(sqlBuilder, queryWrapper, allTables); 573 574 // ignore orderBy and limit 575 buildOrderBySql(sqlBuilder, queryWrapper, allTables); 576 577 Long limitRows = CPI.getLimitRows(queryWrapper); 578 Long limitOffset = CPI.getLimitOffset(queryWrapper); 579 if (limitRows != null || limitOffset != null) { 580 sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset); 581 } 582 583 List<String> endFragments = CPI.getEndFragments(queryWrapper); 584 if (CollectionUtil.isNotEmpty(endFragments)) { 585 for (String endFragment : endFragments) { 586 sqlBuilder.append(BLANK).append(endFragment); 587 } 588 } 589 590 return sqlBuilder.toString(); 591 } 592 593 594 @Override 595 public String buildWhereConditionSql(QueryWrapper queryWrapper) { 596 QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper); 597 return whereQueryCondition != null ? whereQueryCondition.toSql(CPI.getQueryTables(queryWrapper), this) : EMPTY; 598 } 599 600 601 @Override 602 public String forInsertEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) { 603 StringBuilder sql = new StringBuilder(); 604 sql.append(INSERT_INTO).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.INSERT)); 605 606 String[] insertColumns = tableInfo.obtainInsertColumns(entity, ignoreNulls); 607 Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns(); 608 609 Map<String, RawValue> rawValueMap = tableInfo.obtainUpdateRawValueMap(entity); 610 611 StringJoiner sqlFields = new StringJoiner(DELIMITER); 612 StringJoiner sqlValues = new StringJoiner(DELIMITER); 613 614 for (String insertColumn : insertColumns) { 615 sqlFields.add(wrap(insertColumn)); 616 if (rawValueMap.containsKey(insertColumn)) { 617 sqlValues.add(rawValueMap.remove(insertColumn).toSql(this)); 618 } else if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) { 619 sqlValues.add(onInsertColumns.get(insertColumn)); 620 } else { 621 sqlValues.add(PLACEHOLDER); 622 } 623 } 624 625 rawValueMap.forEach((k, v) -> { 626 sqlFields.add(wrap(k)); 627 sqlValues.add(v.toSql(this)); 628 }); 629 630 return sql.append(BRACKET_LEFT).append(sqlFields).append(BRACKET_RIGHT) 631 .append(VALUES) 632 .append(BRACKET_LEFT).append(sqlValues).append(BRACKET_RIGHT) 633 .toString(); 634 } 635 636 637 @Override 638 public String forInsertEntityWithPk(TableInfo tableInfo, Object entity, boolean ignoreNulls) { 639 640 StringBuilder sql = new StringBuilder(); 641 sql.append(INSERT_INTO).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.INSERT)); 642 643 String[] insertColumns = tableInfo.obtainInsertColumnsWithPk(entity, ignoreNulls); 644 Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns(); 645 646 StringJoiner sqlFields = new StringJoiner(DELIMITER); 647 StringJoiner sqlValues = new StringJoiner(DELIMITER); 648 649 for (String insertColumn : insertColumns) { 650 sqlFields.add(wrap(insertColumn)); 651 if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) { 652 sqlValues.add(onInsertColumns.get(insertColumn)); 653 } else { 654 sqlValues.add(PLACEHOLDER); 655 } 656 } 657 658 return sql.append(BRACKET_LEFT).append(sqlFields).append(BRACKET_RIGHT) 659 .append(VALUES) 660 .append(BRACKET_LEFT).append(sqlValues).append(BRACKET_RIGHT) 661 .toString(); 662 } 663 664 665 @Override 666 public String forInsertEntityBatch(TableInfo tableInfo, Collection<?> entities) { 667 StringBuilder sql = new StringBuilder(); 668 sql.append(INSERT_INTO).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.INSERT)); 669 String[] insertColumns = tableInfo.obtainInsertColumns(null, false); 670 String[] warpedInsertColumns = new String[insertColumns.length]; 671 for (int i = 0; i < insertColumns.length; i++) { 672 warpedInsertColumns[i] = wrap(insertColumns[i]); 673 } 674 sql.append(BRACKET_LEFT) 675 .append(StringUtil.join(DELIMITER, warpedInsertColumns)) 676 .append(BRACKET_RIGHT); 677 sql.append(VALUES); 678 679 Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns(); 680 for (int i = 0; i < entities.size(); i++) { 681 StringJoiner stringJoiner = new StringJoiner(DELIMITER, BRACKET_LEFT, BRACKET_RIGHT); 682 for (String insertColumn : insertColumns) { 683 if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) { 684 // 直接读取 onInsert 配置的值,而不用 "?" 代替 685 stringJoiner.add(onInsertColumns.get(insertColumn)); 686 } else { 687 stringJoiner.add(PLACEHOLDER); 688 } 689 } 690 sql.append(stringJoiner); 691 if (i != entities.size() - 1) { 692 sql.append(DELIMITER); 693 } 694 } 695 696 return sql.toString(); 697 } 698 699 @Override 700 public String forDeleteEntityById(TableInfo tableInfo) { 701 String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip(); 702 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 703 704 String[] primaryKeys = tableInfo.getPrimaryColumns(); 705 assertPrimaryKeysNotEmpty(primaryKeys); 706 707 // 正常删除 708 if (StringUtil.noText(logicDeleteColumn)) { 709 String deleteByIdSql = forDeleteById(tableInfo.getSchema(), tableInfo.getTableName(), primaryKeys); 710 return tableInfo.buildTenantCondition(deleteByIdSql, tenantIdArgs, this); 711 } 712 713 // 逻辑删除 714 StringBuilder sql = new StringBuilder(); 715 sql.append(UPDATE).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE)); 716 sql.append(SET).append(buildLogicDeletedSet(logicDeleteColumn, tableInfo)); 717 sql.append(WHERE); 718 for (int i = 0; i < primaryKeys.length; i++) { 719 if (i > 0) { 720 sql.append(AND); 721 } 722 sql.append(wrap(primaryKeys[i])).append(EQUALS_PLACEHOLDER); 723 } 724 725 sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo)); 726 727 // 租户ID 728 tableInfo.buildTenantCondition(sql, tenantIdArgs, this); 729 prepareAuth(tableInfo, sql, OperateType.DELETE); 730 return sql.toString(); 731 } 732 733 734 @Override 735 public String forDeleteEntityBatchByIds(TableInfo tableInfo, Object[] primaryValues) { 736 String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip(); 737 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 738 739 String[] primaryKeys = tableInfo.getPrimaryColumns(); 740 assertPrimaryKeysNotEmpty(primaryKeys); 741 742 // 正常删除 743 if (StringUtil.noText(logicDeleteColumn)) { 744 String deleteSQL = forDeleteBatchByIds(tableInfo.getSchema(), tableInfo.getTableName(), primaryKeys, primaryValues); 745 746 // 多租户 747 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 748 deleteSQL = deleteSQL.replace(WHERE, WHERE + BRACKET_LEFT) + BRACKET_RIGHT; 749 deleteSQL = tableInfo.buildTenantCondition(deleteSQL, tenantIdArgs, this); 750 } 751 return deleteSQL; 752 } 753 754 StringBuilder sql = new StringBuilder(); 755 sql.append(UPDATE); 756 sql.append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE)); 757 sql.append(SET).append(buildLogicDeletedSet(logicDeleteColumn, tableInfo)); 758 sql.append(WHERE); 759 sql.append(BRACKET_LEFT); 760 761 // 多主键的场景 762 if (primaryKeys.length > 1) { 763 for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) { 764 if (i > 0) { 765 sql.append(OR); 766 } 767 sql.append(BRACKET_LEFT); 768 for (int j = 0; j < primaryKeys.length; j++) { 769 if (j > 0) { 770 sql.append(AND); 771 } 772 sql.append(wrap(primaryKeys[j])).append(EQUALS_PLACEHOLDER); 773 } 774 sql.append(BRACKET_RIGHT); 775 } 776 } 777 // 单主键 778 else { 779 for (int i = 0; i < primaryValues.length; i++) { 780 if (i > 0) { 781 sql.append(OR); 782 } 783 sql.append(wrap(primaryKeys[0])).append(EQUALS_PLACEHOLDER); 784 } 785 } 786 787 sql.append(BRACKET_RIGHT).append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo)); 788 789 tableInfo.buildTenantCondition(sql, tenantIdArgs, this); 790 prepareAuth(tableInfo, sql, OperateType.DELETE); 791 return sql.toString(); 792 } 793 794 @Override 795 public String forDeleteEntityBatchByQuery(TableInfo tableInfo, QueryWrapper queryWrapper) { 796 797 String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip(); 798 799 // 正常删除 800 if (StringUtil.noText(logicDeleteColumn)) { 801 return forDeleteByQuery(queryWrapper); 802 } 803 804 805 prepareAuth(queryWrapper, OperateType.DELETE); 806 // 逻辑删除 807 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 808 List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper); 809 List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables); 810 811 // ignore selectColumns 812 StringBuilder sqlBuilder = new StringBuilder(UPDATE).append(forHint(CPI.getHint(queryWrapper))); 813 sqlBuilder.append(tableInfo.getWrapSchemaAndTableName(this, OperateType.DELETE)); 814 sqlBuilder.append(SET).append(buildLogicDeletedSet(logicDeleteColumn, tableInfo)); 815 816 817 buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.DELETE); 818 buildWhereSql(sqlBuilder, queryWrapper, allTables, false); 819 buildGroupBySql(sqlBuilder, queryWrapper, allTables); 820 buildHavingSql(sqlBuilder, queryWrapper, allTables); 821 822 // ignore orderBy and limit 823 // buildOrderBySql(sqlBuilder, queryWrapper) 824 // buildLimitSql(sqlBuilder, queryWrapper) 825 826 return sqlBuilder.toString(); 827 } 828 829 830 @Override 831 public String forUpdateEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) { 832 StringBuilder sql = new StringBuilder(); 833 834 Set<String> updateColumns = tableInfo.obtainUpdateColumns(entity, ignoreNulls, false); 835 Map<String, RawValue> rawValueMap = tableInfo.obtainUpdateRawValueMap(entity); 836 String[] primaryKeys = tableInfo.getPrimaryColumns(); 837 assertPrimaryKeysNotEmpty(primaryKeys); 838 839 sql.append(UPDATE).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE)).append(SET); 840 841 StringJoiner stringJoiner = new StringJoiner(DELIMITER); 842 843 for (String updateColumn : updateColumns) { 844 if (rawValueMap.containsKey(updateColumn)) { 845 stringJoiner.add(wrap(updateColumn) + EQUALS + rawValueMap.get(updateColumn).toSql(this)); 846 } else { 847 stringJoiner.add(wrap(updateColumn) + EQUALS_PLACEHOLDER); 848 } 849 } 850 851 Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns(); 852 if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) { 853 onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + EQUALS + value)); 854 } 855 856 // 乐观锁字段 857 String versionColumn = tableInfo.getVersionColumn(); 858 if (StringUtil.hasText(tableInfo.getOptimisticLockColumnOrSkip())) { 859 stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 "); 860 } 861 862 sql.append(stringJoiner); 863 864 sql.append(WHERE); 865 for (int i = 0; i < primaryKeys.length; i++) { 866 if (i > 0) { 867 sql.append(AND); 868 } 869 sql.append(wrap(primaryKeys[i])).append(EQUALS_PLACEHOLDER); 870 } 871 872 // 逻辑删除条件,已删除的数据不能被修改 873 String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip(); 874 if (StringUtil.hasText(logicDeleteColumn)) { 875 sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo)); 876 } 877 878 879 // 租户ID字段 880 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 881 tableInfo.buildTenantCondition(sql, tenantIdArgs, this); 882 883 // 乐观锁条件 884 if (StringUtil.hasText(tableInfo.getOptimisticLockColumnOrSkip())) { 885 Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn); 886 if (versionValue == null) { 887 throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity); 888 } 889 sql.append(AND).append(wrap(versionColumn)).append(EQUALS).append(versionValue); 890 } 891 892 prepareAuth(tableInfo, sql, OperateType.UPDATE); 893 return sql.toString(); 894 } 895 896 @Override 897 public String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper) { 898 prepareAuth(queryWrapper, OperateType.UPDATE); 899 StringBuilder sqlBuilder = new StringBuilder(); 900 901 Set<String> updateColumns = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true); 902 Map<String, RawValue> rawValueMap = tableInfo.obtainUpdateRawValueMap(entity); 903 904 sqlBuilder.append(UPDATE).append(forHint(CPI.getHint(queryWrapper))); 905 sqlBuilder.append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE)); 906 907 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 908 List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper); 909 List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables); 910 buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.UPDATE); 911 912 913 sqlBuilder.append(SET); 914 915 StringJoiner stringJoiner = new StringJoiner(DELIMITER); 916 917 for (String modifyAttr : updateColumns) { 918 if (rawValueMap.containsKey(modifyAttr)) { 919 stringJoiner.add(wrap(modifyAttr) + EQUALS + rawValueMap.get(modifyAttr).toSql(this)); 920 } else { 921 stringJoiner.add(wrap(modifyAttr) + EQUALS_PLACEHOLDER); 922 } 923 } 924 925 926 Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns(); 927 if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) { 928 onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + EQUALS + value)); 929 } 930 931 // 乐观锁字段 932 String versionColumn = tableInfo.getVersionColumn(); 933 if (StringUtil.hasText(tableInfo.getOptimisticLockColumnOrSkip())) { 934 stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 "); 935 } 936 937 sqlBuilder.append(stringJoiner); 938 939 940 buildWhereSql(sqlBuilder, queryWrapper, queryTables, false); 941 buildGroupBySql(sqlBuilder, queryWrapper, queryTables); 942 buildHavingSql(sqlBuilder, queryWrapper, queryTables); 943 944 // ignore orderBy and limit 945 buildOrderBySql(sqlBuilder, queryWrapper, queryTables); 946 947 Long limitRows = CPI.getLimitRows(queryWrapper); 948 Long limitOffset = CPI.getLimitOffset(queryWrapper); 949 if (limitRows != null || limitOffset != null) { 950 sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset); 951 } 952 953 954 List<String> endFragments = CPI.getEndFragments(queryWrapper); 955 if (CollectionUtil.isNotEmpty(endFragments)) { 956 for (String endFragment : endFragments) { 957 sqlBuilder.append(BLANK).append(endFragment); 958 } 959 } 960 961 return sqlBuilder.toString(); 962 } 963 964 965 @Override 966 public String forSelectOneEntityById(TableInfo tableInfo) { 967 StringBuilder sql = new StringBuilder(); 968 buildSelectColumnSql(sql, null, null, null); 969 sql.append(FROM).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.SELECT)); 970 sql.append(WHERE); 971 String[] pKeys = tableInfo.getPrimaryColumns(); 972 assertPrimaryKeysNotEmpty(pKeys); 973 974 for (int i = 0; i < pKeys.length; i++) { 975 if (i > 0) { 976 sql.append(AND); 977 } 978 sql.append(wrap(pKeys[i])).append(EQUALS_PLACEHOLDER); 979 } 980 981 // 逻辑删除的情况下,需要添加逻辑删除的条件 982 String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip(); 983 if (StringUtil.hasText(logicDeleteColumn)) { 984 sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo)); 985 } 986 987 // 多租户 988 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 989 tableInfo.buildTenantCondition(sql, tenantIdArgs, this); 990 prepareAuth(tableInfo, sql, OperateType.SELECT); 991 return sql.toString(); 992 } 993 994 995 @Override 996 public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) { 997 StringBuilder sql = new StringBuilder(); 998 buildSelectColumnSql(sql, null, tableInfo.getDefaultQueryColumn(), null); 999 sql.append(FROM).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.SELECT)); 1000 sql.append(WHERE); 1001 String[] primaryKeys = tableInfo.getPrimaryColumns(); 1002 assertPrimaryKeysNotEmpty(primaryKeys); 1003 1004 String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip(); 1005 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 1006 if (StringUtil.hasText(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) { 1007 sql.append(BRACKET_LEFT); 1008 } 1009 1010 // 多主键的场景 1011 if (primaryKeys.length > 1) { 1012 for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) { 1013 if (i > 0) { 1014 sql.append(OR); 1015 } 1016 sql.append(BRACKET_LEFT); 1017 for (int j = 0; j < primaryKeys.length; j++) { 1018 if (j > 0) { 1019 sql.append(AND); 1020 } 1021 sql.append(wrap(primaryKeys[j])).append(EQUALS_PLACEHOLDER); 1022 } 1023 sql.append(BRACKET_RIGHT); 1024 } 1025 } 1026 // 单主键 1027 else { 1028 for (int i = 0; i < primaryValues.length; i++) { 1029 if (i > 0) { 1030 sql.append(OR); 1031 } 1032 sql.append(wrap(primaryKeys[0])).append(EQUALS_PLACEHOLDER); 1033 } 1034 } 1035 1036 if (StringUtil.hasText(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) { 1037 sql.append(BRACKET_RIGHT); 1038 } 1039 1040 1041 if (StringUtil.hasText(logicDeleteColumn)) { 1042 sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo)); 1043 } 1044 1045 // 多租户 1046 tableInfo.buildTenantCondition(sql, tenantIdArgs, this); 1047 prepareAuth(tableInfo, sql, OperateType.SELECT); 1048 return sql.toString(); 1049 } 1050 1051 1052 protected boolean buildJoinSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables, OperateType operateType) { 1053 List<Join> joins = CPI.getJoins(queryWrapper); 1054 boolean joinSuccess = false; 1055 if (joins != null && !joins.isEmpty()) { 1056 for (Join join : joins) { 1057 if (!join.checkEffective()) { 1058 continue; 1059 } 1060 sqlBuilder.append(join.toSql(queryTables, this, operateType)); 1061 joinSuccess = true; 1062 } 1063 } 1064 return joinSuccess; 1065 } 1066 1067 1068 protected void buildWhereSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables, boolean allowNoCondition) { 1069 QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper); 1070 if (whereQueryCondition != null) { 1071 String whereSql = whereQueryCondition.toSql(queryTables, this); 1072 if (StringUtil.hasText(whereSql)) { 1073 sqlBuilder.append(WHERE).append(whereSql); 1074 } else if (!allowNoCondition) { 1075 throw FlexExceptions.wrap(LocalizedFormats.UPDATE_OR_DELETE_NOT_ALLOW); 1076 } 1077 } else { 1078 // whereQueryCondition == null 1079 if (!allowNoCondition) { 1080 throw FlexExceptions.wrap(LocalizedFormats.UPDATE_OR_DELETE_NOT_ALLOW); 1081 } 1082 } 1083 } 1084 1085 1086 protected void buildGroupBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) { 1087 List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper); 1088 if (groupByColumns != null && !groupByColumns.isEmpty()) { 1089 sqlBuilder.append(GROUP_BY); 1090 int index = 0; 1091 for (QueryColumn groupByColumn : groupByColumns) { 1092 String groupBy = CPI.toConditionSql(groupByColumn, queryTables, this); 1093 sqlBuilder.append(groupBy); 1094 if (index != groupByColumns.size() - 1) { 1095 sqlBuilder.append(DELIMITER); 1096 } 1097 index++; 1098 } 1099 } 1100 } 1101 1102 1103 protected void buildHavingSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) { 1104 QueryCondition havingQueryCondition = CPI.getHavingQueryCondition(queryWrapper); 1105 if (havingQueryCondition != null) { 1106 String havingSql = havingQueryCondition.toSql(queryTables, this); 1107 if (StringUtil.hasText(havingSql)) { 1108 sqlBuilder.append(HAVING).append(havingSql); 1109 } 1110 } 1111 } 1112 1113 1114 protected void buildOrderBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) { 1115 List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper); 1116 if (orderBys != null && !orderBys.isEmpty()) { 1117 sqlBuilder.append(ORDER_BY); 1118 int index = 0; 1119 for (QueryOrderBy orderBy : orderBys) { 1120 sqlBuilder.append(orderBy.toSql(queryTables, this)); 1121 if (index != orderBys.size() - 1) { 1122 sqlBuilder.append(DELIMITER); 1123 } 1124 index++; 1125 } 1126 } 1127 } 1128 1129 1130 /** 1131 * 构建 limit 和 offset 的参数 1132 */ 1133 protected StringBuilder buildLimitOffsetSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, Long limitRows, Long limitOffset) { 1134 return limitOffsetProcessor.process(this, sqlBuilder, queryWrapper, limitRows, limitOffset); 1135 } 1136 1137 1138 protected String buildLogicNormalCondition(String logicColumn, TableInfo tableInfo) { 1139 return LogicDeleteManager.getProcessor().buildLogicNormalCondition(logicColumn, tableInfo, this); 1140 } 1141 1142 1143 protected String buildLogicDeletedSet(String logicColumn, TableInfo tableInfo) { 1144 return LogicDeleteManager.getProcessor().buildLogicDeletedSet(logicColumn, tableInfo, this); 1145 } 1146 1147 /** 1148 * 断言主键非空 1149 * 1150 * @param primaryKeys 主键 1151 */ 1152 protected void assertPrimaryKeysNotEmpty(String[] primaryKeys) { 1153 if (Objects.isNull(primaryKeys) || primaryKeys.length == 0 || Arrays.stream(primaryKeys).allMatch(String::isEmpty)) { 1154 throw FlexExceptions.wrap("primary key not recognized! Please check the @com.mybatisflex.annotation.Id annotation"); 1155 } 1156 } 1157}