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.FlexGlobalConfig; 019import com.mybatisflex.core.dialect.IDialect; 020import com.mybatisflex.core.dialect.KeywordWrap; 021import com.mybatisflex.core.dialect.LimitOffsetProcessor; 022import com.mybatisflex.core.exception.FlexExceptions; 023import com.mybatisflex.core.query.*; 024import com.mybatisflex.core.row.Row; 025import com.mybatisflex.core.row.RowCPI; 026import com.mybatisflex.core.table.TableInfo; 027import com.mybatisflex.core.util.ArrayUtil; 028import com.mybatisflex.core.util.CollectionUtil; 029import com.mybatisflex.core.util.StringUtil; 030 031import java.util.List; 032import java.util.Map; 033import java.util.Set; 034import java.util.StringJoiner; 035 036/** 037 * 通用的方言设计,其他方言可以继承于当前 CommonsDialectImpl 038 * 创建或获取方言请参考 {@link com.mybatisflex.core.dialect.DialectFactory} 039 */ 040public class CommonsDialectImpl implements IDialect { 041 042 protected KeywordWrap keywordWrap = KeywordWrap.BACKQUOTE; 043 private LimitOffsetProcessor limitOffsetProcessor = LimitOffsetProcessor.MYSQL; 044 045 public CommonsDialectImpl() { 046 } 047 048 public CommonsDialectImpl(LimitOffsetProcessor limitOffsetProcessor) { 049 this.limitOffsetProcessor = limitOffsetProcessor; 050 } 051 052 public CommonsDialectImpl(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) { 053 this.keywordWrap = keywordWrap; 054 this.limitOffsetProcessor = limitOffsetProcessor; 055 } 056 057 @Override 058 public String wrap(String keyword) { 059 return "*".equals(keyword) ? keyword : keywordWrap.wrap(keyword); 060 } 061 062 063 @Override 064 public String forHint(String hintString) { 065 return StringUtil.isNotBlank(hintString) ? "/*+ " + hintString + " */ " : ""; 066 } 067 068 @Override 069 public String forInsertRow(String schema, String tableName, Row row) { 070 StringBuilder fields = new StringBuilder(); 071 StringBuilder questions = new StringBuilder(); 072 073 Set<String> attrs = row.obtainModifyAttrs(); 074 int index = 0; 075 for (String attr : attrs) { 076 fields.append(wrap(attr)); 077 questions.append("?"); 078 if (index != attrs.size() - 1) { 079 fields.append(", "); 080 questions.append(", "); 081 } 082 index++; 083 } 084 StringBuilder sql = new StringBuilder(); 085 sql.append("INSERT INTO "); 086 if (StringUtil.isNotBlank(schema)) { 087 sql.append(wrap(getRealSchema(schema))).append("."); 088 } 089 sql.append(wrap(getRealTable(tableName))); 090 sql.append("(").append(fields).append(") "); 091 sql.append(" VALUES ").append("(").append(questions).append(")"); 092 return sql.toString(); 093 } 094 095 096 @Override 097 public String forInsertBatchWithFirstRowColumns(String schema, String tableName, List<Row> rows) { 098 StringBuilder fields = new StringBuilder(); 099 StringBuilder questions = new StringBuilder(); 100 101 Row firstRow = rows.get(0); 102 Set<String> attrs = firstRow.obtainModifyAttrs(); 103 int index = 0; 104 for (String column : attrs) { 105 fields.append(wrap(column)); 106 if (index != attrs.size() - 1) { 107 fields.append(", "); 108 } 109 index++; 110 } 111 112 for (int i = 0; i < rows.size(); i++) { 113 questions.append(buildQuestion(attrs.size(), true)); 114 if (i != rows.size() - 1) { 115 questions.append(","); 116 } 117 } 118 119 120 StringBuilder sql = new StringBuilder(); 121 sql.append("INSERT INTO "); 122 if (StringUtil.isNotBlank(schema)) { 123 sql.append(wrap(getRealSchema(schema))).append("."); 124 } 125 sql.append(wrap(getRealTable(tableName))); 126 sql.append(" (").append(fields).append(") "); 127 sql.append(" VALUES ").append(questions); 128 return sql.toString(); 129 } 130 131 132 @Override 133 public String forDeleteById(String schema, String tableName, String[] primaryKeys) { 134 StringBuilder sql = new StringBuilder(); 135 sql.append("DELETE FROM "); 136 if (StringUtil.isNotBlank(schema)) { 137 sql.append(wrap(getRealSchema(schema))).append("."); 138 } 139 sql.append(wrap(getRealTable(tableName))); 140 sql.append(" WHERE "); 141 for (int i = 0; i < primaryKeys.length; i++) { 142 if (i > 0) { 143 sql.append(" AND "); 144 } 145 sql.append(wrap(primaryKeys[i])).append(" = ?"); 146 } 147 return sql.toString(); 148 } 149 150 151 @Override 152 public String forDeleteBatchByIds(String schema, String tableName, String[] primaryKeys, Object[] ids) { 153 StringBuilder sql = new StringBuilder(); 154 sql.append("DELETE FROM "); 155 if (StringUtil.isNotBlank(schema)) { 156 sql.append(wrap(getRealSchema(schema))).append("."); 157 } 158 sql.append(wrap(getRealTable(tableName))); 159 sql.append(" WHERE "); 160 161 //多主键的场景 162 if (primaryKeys.length > 1) { 163 for (int i = 0; i < ids.length / primaryKeys.length; i++) { 164 if (i > 0) { 165 sql.append(" OR "); 166 } 167 sql.append("("); 168 for (int j = 0; j < primaryKeys.length; j++) { 169 if (j > 0) { 170 sql.append(" AND "); 171 } 172 sql.append(wrap(primaryKeys[j])).append(" = ?"); 173 } 174 sql.append(")"); 175 } 176 } 177 // 单主键 178 else { 179 for (int i = 0; i < ids.length; i++) { 180 if (i > 0) { 181 sql.append(" OR "); 182 } 183 sql.append(wrap(primaryKeys[0])).append(" = ?"); 184 } 185 } 186 return sql.toString(); 187 } 188 189 @Override 190 public String forDeleteByQuery(QueryWrapper queryWrapper) { 191 return buildDeleteSql(queryWrapper); 192 } 193 194 @Override 195 public String forUpdateById(String schema, String tableName, Row row) { 196 StringBuilder sql = new StringBuilder(); 197 198 Set<String> modifyAttrs = row.obtainModifyAttrs(); 199 String[] primaryKeys = RowCPI.obtainsPrimaryKeyStrings(row); 200 201 sql.append("UPDATE "); 202 if (StringUtil.isNotBlank(schema)) { 203 sql.append(wrap(getRealSchema(schema))).append("."); 204 } 205 sql.append(wrap(getRealTable(tableName))).append(" SET "); 206 int index = 0; 207 for (Map.Entry<String, Object> e : row.entrySet()) { 208 String colName = e.getKey(); 209 if (modifyAttrs.contains(colName) && !ArrayUtil.contains(primaryKeys, colName)) { 210 if (index > 0) { 211 sql.append(", "); 212 } 213 sql.append(wrap(colName)).append(" = ? "); 214 index++; 215 } 216 } 217 sql.append(" WHERE "); 218 for (int i = 0; i < primaryKeys.length; i++) { 219 if (i > 0) { 220 sql.append(" AND "); 221 } 222 sql.append(wrap(primaryKeys[i])).append(" = ?"); 223 } 224 225 return sql.toString(); 226 } 227 228 @Override 229 public String forUpdateByQuery(QueryWrapper queryWrapper, Row row) { 230 StringBuilder sql = new StringBuilder(); 231 232 Set<String> modifyAttrs = row.obtainModifyAttrs(); 233 234 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 235 if (queryTables == null || queryTables.size() != 1) { 236 throw FlexExceptions.wrap("update sql must need 1 table."); 237 } 238 239 //fix: support schema 240 QueryTable queryTable = queryTables.get(0); 241// String tableName = queryTables.get(0).getName(); 242 sql.append("UPDATE ").append(queryTable.toSql(this)).append(" SET "); 243 int index = 0; 244 for (String modifyAttr : modifyAttrs) { 245 if (index > 0) { 246 sql.append(", "); 247 } 248 sql.append(wrap(modifyAttr)).append(" = ? "); 249 index++; 250 } 251 252 String whereConditionSql = buildWhereConditionSql(queryWrapper); 253 if (StringUtil.isNotBlank(whereConditionSql)) { 254 sql.append(" WHERE ").append(whereConditionSql); 255 } 256 257 return sql.toString(); 258 } 259 260 @Override 261 public String forUpdateBatchById(String schema, String tableName, List<Row> rows) { 262 if (rows.size() == 1) { 263 return forUpdateById(schema, tableName, rows.get(0)); 264 } 265 StringBuilder sql = new StringBuilder(); 266 for (Row row : rows) { 267 sql.append(forUpdateById(schema, tableName, row)).append("; "); 268 } 269 return sql.toString(); 270 } 271 272 273 @Override 274 public String forSelectOneById(String schema, String tableName, String[] primaryKeys, Object[] primaryValues) { 275 StringBuilder sql = new StringBuilder("SELECT * FROM "); 276 if (StringUtil.isNotBlank(schema)) { 277 sql.append(wrap(getRealSchema(schema))).append("."); 278 } 279 sql.append(wrap(getRealTable(tableName))).append(" WHERE "); 280 for (int i = 0; i < primaryKeys.length; i++) { 281 if (i > 0) { 282 sql.append(" AND "); 283 } 284 sql.append(wrap(primaryKeys[i])).append(" = ?"); 285 } 286 return sql.toString(); 287 } 288 289 @Override 290 public String forSelectByQuery(QueryWrapper queryWrapper) { 291 return buildSelectSql(queryWrapper); 292 } 293 294 295 ////////////build query sql/////// 296 @Override 297 public String buildSelectSql(QueryWrapper queryWrapper) { 298 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 299 List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper); 300 List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables); 301 302 List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper); 303 304 StringBuilder sqlBuilder = buildSelectColumnSql(allTables, selectColumns, CPI.getHint(queryWrapper)); 305 sqlBuilder.append(" FROM ").append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this))); 306 307 buildJoinSql(sqlBuilder, queryWrapper, allTables); 308 buildWhereSql(sqlBuilder, queryWrapper, allTables, true); 309 buildGroupBySql(sqlBuilder, queryWrapper, allTables); 310 buildHavingSql(sqlBuilder, queryWrapper, allTables); 311 buildOrderBySql(sqlBuilder, queryWrapper, allTables); 312 313 List<UnionWrapper> unions = CPI.getUnions(queryWrapper); 314 if (CollectionUtil.isNotEmpty(unions)) { 315 sqlBuilder.insert(0, "(").append(")"); 316 for (UnionWrapper unionWrapper : unions) { 317 unionWrapper.buildSql(sqlBuilder, this); 318 } 319 } 320 321 Integer limitRows = CPI.getLimitRows(queryWrapper); 322 Integer limitOffset = CPI.getLimitOffset(queryWrapper); 323 if (limitRows != null || limitOffset != null) { 324 sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset); 325 } 326 327 List<String> endFragments = CPI.getEndFragments(queryWrapper); 328 if (CollectionUtil.isNotEmpty(endFragments)) { 329 for (String endFragment : endFragments) { 330 sqlBuilder.append(" ").append(endFragment); 331 } 332 } 333 334 return sqlBuilder.toString(); 335 } 336 337 private StringBuilder buildSelectColumnSql(List<QueryTable> queryTables, List<QueryColumn> selectColumns, String hint) { 338 StringBuilder sqlBuilder = new StringBuilder("SELECT "); 339 sqlBuilder.append(forHint(hint)); 340 if (selectColumns == null || selectColumns.isEmpty()) { 341 sqlBuilder.append("*"); 342 } else { 343 int index = 0; 344 for (QueryColumn selectColumn : selectColumns) { 345 String selectColumnSql = CPI.toSelectSql(selectColumn, queryTables, this); 346 sqlBuilder.append(selectColumnSql); 347 if (index != selectColumns.size() - 1) { 348 sqlBuilder.append(", "); 349 } 350 index++; 351 } 352 } 353 return sqlBuilder; 354 } 355 356 357 @Override 358 public String buildDeleteSql(QueryWrapper queryWrapper) { 359 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 360 List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper); 361 List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables); 362 363 //ignore selectColumns 364 StringBuilder sqlBuilder = new StringBuilder("DELETE " + forHint(CPI.getHint(queryWrapper)) + "FROM "); 365 sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this))); 366 367 buildJoinSql(sqlBuilder, queryWrapper, allTables); 368 buildWhereSql(sqlBuilder, queryWrapper, allTables, false); 369 buildGroupBySql(sqlBuilder, queryWrapper, allTables); 370 buildHavingSql(sqlBuilder, queryWrapper, allTables); 371 372 //ignore orderBy and limit 373 //buildOrderBySql(sqlBuilder, queryWrapper); 374 //buildLimitSql(sqlBuilder, queryWrapper); 375 376 List<String> endFragments = CPI.getEndFragments(queryWrapper); 377 if (CollectionUtil.isNotEmpty(endFragments)) { 378 for (String endFragment : endFragments) { 379 sqlBuilder.append(" ").append(endFragment); 380 } 381 } 382 383 return sqlBuilder.toString(); 384 } 385 386 @Override 387 public String buildWhereConditionSql(QueryWrapper queryWrapper) { 388 QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper); 389 return whereQueryCondition != null ? whereQueryCondition.toSql(CPI.getQueryTables(queryWrapper), this) : ""; 390 } 391 392 @Override 393 public String forInsertEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) { 394 StringBuilder sql = new StringBuilder(); 395 sql.append("INSERT INTO ").append(tableInfo.getWrapSchemaAndTableName(this)); 396 397 String[] insertColumns = tableInfo.obtainInsertColumns(entity, ignoreNulls); 398 Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns(); 399 400 StringJoiner sqlFields = new StringJoiner(", "); 401 StringJoiner sqlValues = new StringJoiner(", "); 402 403 for (String insertColumn : insertColumns) { 404 sqlFields.add(wrap(insertColumn)); 405 if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) { 406 sqlValues.add(onInsertColumns.get(insertColumn)); 407 } else { 408 sqlValues.add("?"); 409 } 410 } 411 412 return sql.append("(").append(sqlFields).append(")") 413 .append(" VALUES ") 414 .append("(").append(sqlValues).append(")").toString(); 415 } 416 417 @Override 418 public String forInsertEntityBatch(TableInfo tableInfo, List<?> entities) { 419 StringBuilder sql = new StringBuilder(); 420 sql.append("INSERT INTO ").append(tableInfo.getWrapSchemaAndTableName(this)); 421 String[] insertColumns = tableInfo.obtainInsertColumns(null, false); 422 String[] warpedInsertColumns = new String[insertColumns.length]; 423 for (int i = 0; i < insertColumns.length; i++) { 424 warpedInsertColumns[i] = wrap(insertColumns[i]); 425 } 426 sql.append("(").append(StringUtil.join(", ", warpedInsertColumns)).append(")"); 427 sql.append(" VALUES "); 428 429 Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns(); 430 for (int i = 0; i < entities.size(); i++) { 431 StringJoiner stringJoiner = new StringJoiner(", ", "(", ")"); 432 for (String insertColumn : insertColumns) { 433 if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) { 434 //直接读取 onInsert 配置的值,而不用 "?" 代替 435 stringJoiner.add(onInsertColumns.get(insertColumn)); 436 } else { 437 stringJoiner.add("?"); 438 } 439 } 440 sql.append(stringJoiner); 441 if (i != entities.size() - 1) { 442 sql.append(", "); 443 } 444 } 445 446 return sql.toString(); 447 } 448 449 @Override 450 public String forDeleteEntityById(TableInfo tableInfo) { 451 String logicDeleteColumn = tableInfo.getLogicDeleteColumn(); 452 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 453 //正常删除 454 if (StringUtil.isBlank(logicDeleteColumn)) { 455 String deleteByIdSql = forDeleteById(tableInfo.getSchema(), tableInfo.getTableName(), tableInfo.getPrimaryKeys()); 456 457 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 458 deleteByIdSql += " AND " + wrap(tableInfo.getTenantIdColumn()) + " IN " + buildQuestion(tenantIdArgs.length, true); 459 } 460 return deleteByIdSql; 461 } 462 463 //逻辑删除 464 StringBuilder sql = new StringBuilder(); 465 String[] primaryKeys = tableInfo.getPrimaryKeys(); 466 467 sql.append("UPDATE ").append(tableInfo.getWrapSchemaAndTableName(this)); 468 sql.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue()); 469 sql.append(" WHERE "); 470 for (int i = 0; i < primaryKeys.length; i++) { 471 if (i > 0) { 472 sql.append(" AND "); 473 } 474 sql.append(wrap(primaryKeys[i])).append(" = ?"); 475 } 476 477 sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue()); 478 479 //租户ID 480 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 481 sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true)); 482 } 483 484 return sql.toString(); 485 } 486 487 488 @Override 489 public String forDeleteEntityBatchByIds(TableInfo tableInfo, Object[] primaryValues) { 490 String logicDeleteColumn = tableInfo.getLogicDeleteColumn(); 491 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 492 493 //正常删除 494 if (StringUtil.isBlank(logicDeleteColumn)) { 495 String deleteSQL = forDeleteBatchByIds(tableInfo.getSchema(), tableInfo.getTableName(), tableInfo.getPrimaryKeys(), primaryValues); 496 497 //多租户 498 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 499 deleteSQL = deleteSQL.replace(" WHERE ", " WHERE (") + ")"; 500 deleteSQL += " AND " + wrap(tableInfo.getTenantIdColumn()) + " IN " + buildQuestion(tenantIdArgs.length, true); 501 } 502 return deleteSQL; 503 } 504 505 StringBuilder sql = new StringBuilder(); 506 sql.append("UPDATE "); 507 sql.append(tableInfo.getWrapSchemaAndTableName(this)); 508 sql.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue()); 509 sql.append(" WHERE "); 510 sql.append("("); 511 512 String[] primaryKeys = tableInfo.getPrimaryKeys(); 513 514 //多主键的场景 515 if (primaryKeys.length > 1) { 516 for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) { 517 if (i > 0) { 518 sql.append(" OR "); 519 } 520 sql.append("("); 521 for (int j = 0; j < primaryKeys.length; j++) { 522 if (j > 0) { 523 sql.append(" AND "); 524 } 525 sql.append(wrap(primaryKeys[j])).append(" = ?"); 526 } 527 sql.append(")"); 528 } 529 } 530 // 单主键 531 else { 532 for (int i = 0; i < primaryValues.length; i++) { 533 if (i > 0) { 534 sql.append(" OR "); 535 } 536 sql.append(wrap(primaryKeys[0])).append(" = ?"); 537 } 538 } 539 540 sql.append(") AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue()); 541 542 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 543 sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true)); 544 } 545 546 return sql.toString(); 547 } 548 549 @Override 550 public String forDeleteEntityBatchByQuery(TableInfo tableInfo, QueryWrapper queryWrapper) { 551 552 String logicDeleteColumn = tableInfo.getLogicDeleteColumn(); 553 554 //正常删除 555 if (StringUtil.isBlank(logicDeleteColumn)) { 556 return forDeleteByQuery(queryWrapper); 557 } 558 559 560 //逻辑删除 561 List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper); 562 List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper); 563 List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables); 564 565 //ignore selectColumns 566 StringBuilder sqlBuilder = new StringBuilder("UPDATE ").append(forHint(CPI.getHint(queryWrapper))); 567 sqlBuilder.append(tableInfo.getWrapSchemaAndTableName(this)); 568 sqlBuilder.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue()); 569 570 571 buildJoinSql(sqlBuilder, queryWrapper, allTables); 572 buildWhereSql(sqlBuilder, queryWrapper, allTables, false); 573 buildGroupBySql(sqlBuilder, queryWrapper, allTables); 574 buildHavingSql(sqlBuilder, queryWrapper, allTables); 575 576 //ignore orderBy and limit 577 //buildOrderBySql(sqlBuilder, queryWrapper); 578 //buildLimitSql(sqlBuilder, queryWrapper); 579 580 return sqlBuilder.toString(); 581 } 582 583 584 @Override 585 public String forUpdateEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) { 586 StringBuilder sql = new StringBuilder(); 587 588 Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, false); 589 String[] primaryKeys = tableInfo.getPrimaryKeys(); 590 591 sql.append("UPDATE ").append(tableInfo.getWrapSchemaAndTableName(this)).append(" SET "); 592 593 StringJoiner stringJoiner = new StringJoiner(", "); 594 595 for (String modifyAttr : modifyAttrs) { 596 stringJoiner.add(wrap(modifyAttr) + " = ?"); 597 } 598 599 Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns(); 600 if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) { 601 onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + " = " + value)); 602 } 603 604 //乐观锁字段 605 String versionColumn = tableInfo.getVersionColumn(); 606 if (StringUtil.isNotBlank(versionColumn)) { 607 stringJoiner.add(wrap(versionColumn) + " = " + wrap(versionColumn) + " + 1 "); 608 } 609 610 sql.append(stringJoiner); 611 612 sql.append(" WHERE "); 613 for (int i = 0; i < primaryKeys.length; i++) { 614 if (i > 0) { 615 sql.append(" AND "); 616 } 617 sql.append(wrap(primaryKeys[i])).append(" = ?"); 618 } 619 620 //逻辑删除条件,已删除的数据不能被修改 621 String logicDeleteColumn = tableInfo.getLogicDeleteColumn(); 622 if (StringUtil.isNotBlank(logicDeleteColumn)) { 623 sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue()); 624 } 625 626 627 //租户ID字段 628 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 629 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 630 if (tenantIdArgs.length == 1) { 631 sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" = ?"); 632 } else { 633 sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true)); 634 } 635 } 636 637 //乐观锁条件 638 if (StringUtil.isNotBlank(versionColumn)) { 639 Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn); 640 if (versionValue == null) { 641 throw FlexExceptions.wrap("The version value of entity[%s] must not be null.", entity); 642 } 643 sql.append(" AND ").append(wrap(versionColumn)).append(" = ").append(versionValue); 644 } 645 646 647 return sql.toString(); 648 } 649 650 @Override 651 public String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper) { 652 StringBuilder sql = new StringBuilder(); 653 654 Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true); 655 656 sql.append("UPDATE ").append(forHint(CPI.getHint(queryWrapper))) 657 .append(tableInfo.getWrapSchemaAndTableName(this)).append(" SET "); 658 659 StringJoiner stringJoiner = new StringJoiner(", "); 660 661 for (String modifyAttr : modifyAttrs) { 662 stringJoiner.add(wrap(modifyAttr) + " = ?"); 663 } 664 665 Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns(); 666 if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) { 667 onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + " = " + value)); 668 } 669 670 //乐观锁字段 671 String versionColumn = tableInfo.getVersionColumn(); 672 if (StringUtil.isNotBlank(versionColumn)) { 673 stringJoiner.add(wrap(versionColumn) + " = " + wrap(versionColumn) + " + 1 "); 674 } 675 676 sql.append(stringJoiner); 677 678 679 String whereConditionSql = buildWhereConditionSql(queryWrapper); 680 681 //不允许全量更新 682 if (StringUtil.isBlank(whereConditionSql)) { 683 throw new IllegalArgumentException("Not allowed UPDATE a table without where condition."); 684 } 685 686 sql.append(" WHERE ").append(whereConditionSql); 687 688 List<String> endFragments = CPI.getEndFragments(queryWrapper); 689 if (CollectionUtil.isNotEmpty(endFragments)) { 690 for (String endFragment : endFragments) { 691 sql.append(" ").append(endFragment); 692 } 693 } 694 695 return sql.toString(); 696 } 697 698 699 @Override 700 public String forUpdateNumberAddByQuery(String schema, String tableName, String fieldName, Number value, QueryWrapper queryWrapper) { 701 StringBuilder sql = new StringBuilder(); 702 sql.append("UPDATE ").append(forHint(CPI.getHint(queryWrapper))); 703 if (StringUtil.isNotBlank(schema)) { 704 sql.append(wrap(getRealSchema(schema))).append("."); 705 } 706 sql.append(wrap(getRealTable(tableName))).append(" SET "); 707 sql.append(wrap(fieldName)).append("=").append(wrap(fieldName)).append(value.intValue() >= 0 ? " + " : " - ").append(Math.abs(value.longValue())); 708 709 String whereConditionSql = buildWhereConditionSql(queryWrapper); 710 711 //不允许全量更新 712 if (StringUtil.isBlank(whereConditionSql)) { 713 throw new IllegalArgumentException("Not allowed UPDATE a table without where condition."); 714 } 715 716 sql.append(" WHERE ").append(whereConditionSql); 717 718 List<String> endFragments = CPI.getEndFragments(queryWrapper); 719 if (CollectionUtil.isNotEmpty(endFragments)) { 720 for (String endFragment : endFragments) { 721 sql.append(" ").append(endFragment); 722 } 723 } 724 725 return sql.toString(); 726 } 727 728 729 @Override 730 public String forSelectOneEntityById(TableInfo tableInfo) { 731 StringBuilder sql = buildSelectColumnSql(null, null, null); 732 sql.append(" FROM ").append(tableInfo.getWrapSchemaAndTableName(this)); 733 sql.append(" WHERE "); 734 String[] pKeys = tableInfo.getPrimaryKeys(); 735 for (int i = 0; i < pKeys.length; i++) { 736 if (i > 0) { 737 sql.append(" AND "); 738 } 739 sql.append(wrap(pKeys[i])).append(" = ?"); 740 } 741 742 //逻辑删除的情况下,需要添加逻辑删除的条件 743 String logicDeleteColumn = tableInfo.getLogicDeleteColumn(); 744 if (StringUtil.isNotBlank(logicDeleteColumn)) { 745 sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue()); 746 } 747 748 //多租户 749 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 750 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 751 sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true)); 752 } 753 754 return sql.toString(); 755 } 756 757 758 @Override 759 public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) { 760 StringBuilder sql = buildSelectColumnSql(null, tableInfo.getDefaultQueryColumn(), null); 761 sql.append(" FROM ").append(tableInfo.getWrapSchemaAndTableName(this)); 762 sql.append(" WHERE "); 763 String[] primaryKeys = tableInfo.getPrimaryKeys(); 764 765 String logicDeleteColumn = tableInfo.getLogicDeleteColumn(); 766 Object[] tenantIdArgs = tableInfo.buildTenantIdArgs(); 767 if (StringUtil.isNotBlank(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) { 768 sql.append("("); 769 } 770 771 //多主键的场景 772 if (primaryKeys.length > 1) { 773 for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) { 774 if (i > 0) { 775 sql.append(" OR "); 776 } 777 sql.append("("); 778 for (int j = 0; j < primaryKeys.length; j++) { 779 if (j > 0) { 780 sql.append(" AND "); 781 } 782 sql.append(wrap(primaryKeys[j])).append(" = ?"); 783 } 784 sql.append(")"); 785 } 786 } 787 // 单主键 788 else { 789 for (int i = 0; i < primaryValues.length; i++) { 790 if (i > 0) { 791 sql.append(" OR "); 792 } 793 sql.append(wrap(primaryKeys[0])).append(" = ?"); 794 } 795 } 796 797 if (StringUtil.isNotBlank(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) { 798 sql.append(")"); 799 } 800 801 802 if (StringUtil.isNotBlank(logicDeleteColumn)) { 803 sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue()); 804 } 805 806 if (ArrayUtil.isNotEmpty(tenantIdArgs)) { 807 sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN").append(buildQuestion(tenantIdArgs.length, true)); 808 } 809 810 return sql.toString(); 811 } 812 813 814 protected void buildJoinSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) { 815 List<Join> joins = CPI.getJoins(queryWrapper); 816 if (joins != null && !joins.isEmpty()) { 817 for (Join join : joins) { 818 if (!join.checkEffective()) { 819 continue; 820 } 821 sqlBuilder.append(join.toSql(queryTables, this)); 822 } 823 } 824 } 825 826 827 protected void buildWhereSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables, boolean allowNoCondition) { 828 QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper); 829 if (whereQueryCondition != null) { 830 String whereSql = whereQueryCondition.toSql(queryTables, this); 831 if (StringUtil.isNotBlank(whereSql)) { 832 sqlBuilder.append(" WHERE ").append(whereSql); 833 } else if (!allowNoCondition) { 834 throw new IllegalArgumentException("Not allowed DELETE a table without where condition."); 835 } 836 } 837 } 838 839 840 protected void buildGroupBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) { 841 List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper); 842 if (groupByColumns != null && !groupByColumns.isEmpty()) { 843 sqlBuilder.append(" GROUP BY "); 844 int index = 0; 845 for (QueryColumn groupByColumn : groupByColumns) { 846 String groupBy = CPI.toConditionSql(groupByColumn, queryTables, this); 847 sqlBuilder.append(groupBy); 848 if (index != groupByColumns.size() - 1) { 849 sqlBuilder.append(", "); 850 } 851 index++; 852 } 853 } 854 } 855 856 857 protected void buildHavingSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) { 858 QueryCondition havingQueryCondition = CPI.getHavingQueryCondition(queryWrapper); 859 if (havingQueryCondition != null) { 860 String havingSql = havingQueryCondition.toSql(queryTables, this); 861 if (StringUtil.isNotBlank(havingSql)) { 862 sqlBuilder.append(" HAVING ").append(havingSql); 863 } 864 } 865 } 866 867 868 protected void buildOrderBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) { 869 List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper); 870 if (orderBys != null && !orderBys.isEmpty()) { 871 sqlBuilder.append(" ORDER BY "); 872 int index = 0; 873 for (QueryOrderBy orderBy : orderBys) { 874 sqlBuilder.append(orderBy.toSql(queryTables, this)); 875 if (index != orderBys.size() - 1) { 876 sqlBuilder.append(", "); 877 } 878 index++; 879 } 880 } 881 } 882 883 884 /** 885 * 构建 limit 和 offset 的参数 886 */ 887 protected StringBuilder buildLimitOffsetSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, Integer limitRows, Integer limitOffset) { 888 return limitOffsetProcessor.process(sqlBuilder, queryWrapper, limitRows, limitOffset); 889 } 890 891 892 protected String buildQuestion(int count, boolean withBrackets) { 893 StringBuilder sb = new StringBuilder(); 894 for (int i = 0; i < count; i++) { 895 sb.append("?"); 896 if (i != count - 1) { 897 sb.append(", "); 898 } 899 } 900 return withBrackets ? "(" + sb + ")" : sb.toString(); 901 } 902 903 904 protected Object getLogicNormalValue() { 905 Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete(); 906 if (normalValueOfLogicDelete instanceof Number 907 || normalValueOfLogicDelete instanceof Boolean) { 908 return normalValueOfLogicDelete; 909 } 910 return "'" + normalValueOfLogicDelete + "'"; 911 } 912 913 914 protected Object getLogicDeletedValue() { 915 Object deletedValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getDeletedValueOfLogicDelete(); 916 if (deletedValueOfLogicDelete instanceof Number 917 || deletedValueOfLogicDelete instanceof Boolean) { 918 return deletedValueOfLogicDelete; 919 } 920 return "'" + deletedValueOfLogicDelete + "'"; 921 } 922 923}