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