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