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