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