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        return buildSelectSql(queryWrapper, Collections.emptyList());
399    }
400
401    @Override
402    public String buildSelectSql(QueryWrapper queryWrapper, List<QueryTable> contextTables) {
403        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
404
405        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
406        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
407        allTables = CollectionUtil.merge(allTables, contextTables);
408
409        List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
410
411        // 多个表,非 SELECT * 时,需要处理重名字段
412        if (allTables.size() > 1 && selectColumns != null && selectColumns.size() > 1) {
413            IntStream.range(0, selectColumns.size())
414                .boxed()
415                // 生成 索引-字段值 对应关系
416                .collect(Collectors.toMap(Function.identity(), selectColumns::get))
417                .entrySet()
418                .stream()
419                // 需要处理别名的情况
420                .filter(e -> StringUtil.hasText(e.getValue().getName()))
421                .filter(e -> StringUtil.noText(e.getValue().getAlias()))
422                .filter(e -> !"*".equals(e.getValue().getName()))
423                // 将相同字段对象放在一个集合里
424                .collect(Collectors.groupingBy(e -> e.getValue().getName(),
425                    Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)))
426                .values()
427                .stream()
428                // 过滤出来重名的字段
429                .filter(e -> e.size() > 1)
430                // 合并所有需要加别名的字段
431                .flatMap(Collection::stream)
432                // 过滤出来可以添加别名的列
433                .filter(e -> e.getValue().getTable() != null)
434                .filter(e -> StringUtil.hasText(e.getValue().getTable().getName()))
435                // 添加别名并放回原集合索引位置
436                .forEach(e -> selectColumns.set(e.getKey(),
437                    e.getValue().as(e.getValue().getTable().getName() + "$" + e.getValue().getName())));
438        }
439
440        StringBuilder sqlBuilder = new StringBuilder();
441        With with = CPI.getWith(queryWrapper);
442        if (with != null) {
443            sqlBuilder.append(with.toSql(this));
444        }
445
446        buildSelectColumnSql(sqlBuilder, allTables, selectColumns, CPI.getHint(queryWrapper));
447
448
449        if(CollectionUtil.isNotEmpty(queryTables)) {
450            sqlBuilder.append(FROM).append(StringUtil.join(DELIMITER, queryTables, queryTable -> queryTable.toSql(this, OperateType.SELECT)));
451        }
452
453        buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.SELECT);
454        buildWhereSql(sqlBuilder, queryWrapper, allTables, true);
455        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
456        buildHavingSql(sqlBuilder, queryWrapper, allTables);
457        buildOrderBySql(sqlBuilder, queryWrapper, allTables);
458
459        List<UnionWrapper> unions = CPI.getUnions(queryWrapper);
460        if (CollectionUtil.isNotEmpty(unions)) {
461            sqlBuilder.insert(0, BRACKET_LEFT).append(BRACKET_RIGHT);
462            for (UnionWrapper unionWrapper : unions) {
463                unionWrapper.buildSql(sqlBuilder, this);
464            }
465        }
466
467        Long limitRows = CPI.getLimitRows(queryWrapper);
468        Long limitOffset = CPI.getLimitOffset(queryWrapper);
469        if (limitRows != null || limitOffset != null) {
470            sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
471        }
472
473        List<String> endFragments = CPI.getEndFragments(queryWrapper);
474        if (CollectionUtil.isNotEmpty(endFragments)) {
475            for (String endFragment : endFragments) {
476                sqlBuilder.append(BLANK).append(endFragment);
477            }
478        }
479
480        return sqlBuilder.toString();
481    }
482
483    @Override
484    public String buildNoSelectSql(QueryWrapper queryWrapper) {
485        StringBuilder sqlBuilder = new StringBuilder();
486
487        buildJoinSql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST, OperateType.SELECT);
488        buildWhereSql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST, true);
489        buildGroupBySql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST);
490        buildHavingSql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST);
491        buildOrderBySql(sqlBuilder, queryWrapper, Collections.EMPTY_LIST);
492
493        List<UnionWrapper> unions = CPI.getUnions(queryWrapper);
494        if (CollectionUtil.isNotEmpty(unions)) {
495            if (sqlBuilder.length() > 0) {
496                sqlBuilder.insert(0, BRACKET_LEFT).append(BRACKET_RIGHT);
497            }
498            for (UnionWrapper unionWrapper : unions) {
499                unionWrapper.buildSql(sqlBuilder, this);
500            }
501        }
502
503        Long limitRows = CPI.getLimitRows(queryWrapper);
504        Long limitOffset = CPI.getLimitOffset(queryWrapper);
505        if (limitRows != null || limitOffset != null) {
506            sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
507        }
508
509        List<String> endFragments = CPI.getEndFragments(queryWrapper);
510        if (CollectionUtil.isNotEmpty(endFragments)) {
511            for (String endFragment : endFragments) {
512                sqlBuilder.append(BLANK).append(endFragment);
513            }
514        }
515
516        return sqlBuilder.toString();
517    }
518
519    private void buildSelectColumnSql(StringBuilder sqlBuilder, List<QueryTable> queryTables, List<QueryColumn> selectColumns, String hint) {
520        sqlBuilder.append(SELECT);
521        sqlBuilder.append(forHint(hint));
522        if (selectColumns == null || selectColumns.isEmpty()) {
523            sqlBuilder.append(ASTERISK);
524        } else {
525            int index = 0;
526            for (QueryColumn selectColumn : selectColumns) {
527                String selectColumnSql = CPI.toSelectSql(selectColumn, queryTables, this);
528                sqlBuilder.append(selectColumnSql);
529                if (index != selectColumns.size() - 1) {
530                    sqlBuilder.append(DELIMITER);
531                }
532                index++;
533            }
534        }
535    }
536
537
538    @Override
539    public String buildDeleteSql(QueryWrapper queryWrapper) {
540        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
541        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
542        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
543
544        // ignore selectColumns
545        StringBuilder sqlBuilder = new StringBuilder(DELETE);
546        String hint = CPI.getHint(queryWrapper);
547        if (StringUtil.hasText(hint)) {
548            sqlBuilder.append(BLANK).append(hint).deleteCharAt(sqlBuilder.length() - 1);
549        }
550
551        // delete with join
552        if (joinTables != null && !joinTables.isEmpty()) {
553            if (queryTables == null || queryTables.isEmpty()) {
554                throw new IllegalArgumentException("Delete with join sql must designate the from table.");
555            } else if (queryTables.size() != 1) {
556                throw new IllegalArgumentException("Delete with join sql must has 1 table only. but current has " + queryTables.size());
557            }
558            QueryTable queryTable = queryTables.get(0);
559            String table = getRealTable(queryTable.getName(), OperateType.DELETE);
560            if (StringUtil.hasText(queryTable.getSchema())) {
561                sqlBuilder.append(wrap(getRealSchema(queryTable.getSchema(), table, OperateType.DELETE))).append(REFERENCE);
562            }
563            sqlBuilder.append(BLANK).append(wrap(getRealTable(table, OperateType.DELETE)));
564        }
565
566
567        sqlBuilder.append(FROM).append(StringUtil.join(DELIMITER, queryTables, queryTable -> queryTable.toSql(this, OperateType.DELETE)));
568
569        buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.DELETE);
570        buildWhereSql(sqlBuilder, queryWrapper, allTables, false);
571        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
572        buildHavingSql(sqlBuilder, queryWrapper, allTables);
573
574        // ignore orderBy and limit
575        buildOrderBySql(sqlBuilder, queryWrapper, allTables);
576
577        Long limitRows = CPI.getLimitRows(queryWrapper);
578        Long limitOffset = CPI.getLimitOffset(queryWrapper);
579        if (limitRows != null || limitOffset != null) {
580            sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
581        }
582
583        List<String> endFragments = CPI.getEndFragments(queryWrapper);
584        if (CollectionUtil.isNotEmpty(endFragments)) {
585            for (String endFragment : endFragments) {
586                sqlBuilder.append(BLANK).append(endFragment);
587            }
588        }
589
590        return sqlBuilder.toString();
591    }
592
593
594    @Override
595    public String buildWhereConditionSql(QueryWrapper queryWrapper) {
596        QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
597        return whereQueryCondition != null ? whereQueryCondition.toSql(CPI.getQueryTables(queryWrapper), this) : EMPTY;
598    }
599
600
601    @Override
602    public String forInsertEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
603        StringBuilder sql = new StringBuilder();
604        sql.append(INSERT_INTO).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.INSERT));
605
606        String[] insertColumns = tableInfo.obtainInsertColumns(entity, ignoreNulls);
607        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
608
609        Map<String, RawValue> rawValueMap = tableInfo.obtainUpdateRawValueMap(entity);
610
611        StringJoiner sqlFields = new StringJoiner(DELIMITER);
612        StringJoiner sqlValues = new StringJoiner(DELIMITER);
613
614        for (String insertColumn : insertColumns) {
615            sqlFields.add(wrap(insertColumn));
616            if (rawValueMap.containsKey(insertColumn)) {
617                sqlValues.add(rawValueMap.remove(insertColumn).toSql(this));
618            } else if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
619                sqlValues.add(onInsertColumns.get(insertColumn));
620            } else {
621                sqlValues.add(PLACEHOLDER);
622            }
623        }
624
625        rawValueMap.forEach((k, v) -> {
626            sqlFields.add(wrap(k));
627            sqlValues.add(v.toSql(this));
628        });
629
630        return sql.append(BRACKET_LEFT).append(sqlFields).append(BRACKET_RIGHT)
631            .append(VALUES)
632            .append(BRACKET_LEFT).append(sqlValues).append(BRACKET_RIGHT)
633            .toString();
634    }
635
636
637    @Override
638    public String forInsertEntityWithPk(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
639
640        StringBuilder sql = new StringBuilder();
641        sql.append(INSERT_INTO).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.INSERT));
642
643        String[] insertColumns = tableInfo.obtainInsertColumnsWithPk(entity, ignoreNulls);
644        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
645
646        StringJoiner sqlFields = new StringJoiner(DELIMITER);
647        StringJoiner sqlValues = new StringJoiner(DELIMITER);
648
649        for (String insertColumn : insertColumns) {
650            sqlFields.add(wrap(insertColumn));
651            if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
652                sqlValues.add(onInsertColumns.get(insertColumn));
653            } else {
654                sqlValues.add(PLACEHOLDER);
655            }
656        }
657
658        return sql.append(BRACKET_LEFT).append(sqlFields).append(BRACKET_RIGHT)
659            .append(VALUES)
660            .append(BRACKET_LEFT).append(sqlValues).append(BRACKET_RIGHT)
661            .toString();
662    }
663
664
665    @Override
666    public String forInsertEntityBatch(TableInfo tableInfo, Collection<?> entities) {
667        StringBuilder sql = new StringBuilder();
668        sql.append(INSERT_INTO).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.INSERT));
669        String[] insertColumns = tableInfo.obtainInsertColumns(null, false);
670        String[] warpedInsertColumns = new String[insertColumns.length];
671        for (int i = 0; i < insertColumns.length; i++) {
672            warpedInsertColumns[i] = wrap(insertColumns[i]);
673        }
674        sql.append(BRACKET_LEFT)
675            .append(StringUtil.join(DELIMITER, warpedInsertColumns))
676            .append(BRACKET_RIGHT);
677        sql.append(VALUES);
678
679        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
680        for (int i = 0; i < entities.size(); i++) {
681            StringJoiner stringJoiner = new StringJoiner(DELIMITER, BRACKET_LEFT, BRACKET_RIGHT);
682            for (String insertColumn : insertColumns) {
683                if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
684                    // 直接读取 onInsert 配置的值,而不用 "?" 代替
685                    stringJoiner.add(onInsertColumns.get(insertColumn));
686                } else {
687                    stringJoiner.add(PLACEHOLDER);
688                }
689            }
690            sql.append(stringJoiner);
691            if (i != entities.size() - 1) {
692                sql.append(DELIMITER);
693            }
694        }
695
696        return sql.toString();
697    }
698
699    @Override
700    public String forDeleteEntityById(TableInfo tableInfo) {
701        String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip();
702        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
703
704        // 正常删除
705        if (StringUtil.noText(logicDeleteColumn)) {
706            String deleteByIdSql = forDeleteById(tableInfo.getSchema(), tableInfo.getTableName(), tableInfo.getPrimaryColumns());
707            return tableInfo.buildTenantCondition(deleteByIdSql, tenantIdArgs, this);
708        }
709
710        // 逻辑删除
711        StringBuilder sql = new StringBuilder();
712        String[] primaryKeys = tableInfo.getPrimaryColumns();
713
714        sql.append(UPDATE).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE));
715        sql.append(SET).append(buildLogicDeletedSet(logicDeleteColumn, tableInfo));
716        sql.append(WHERE);
717        for (int i = 0; i < primaryKeys.length; i++) {
718            if (i > 0) {
719                sql.append(AND);
720            }
721            sql.append(wrap(primaryKeys[i])).append(EQUALS_PLACEHOLDER);
722        }
723
724        sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo));
725
726        // 租户ID
727        tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
728        prepareAuth(tableInfo, sql, OperateType.DELETE);
729        return sql.toString();
730    }
731
732
733    @Override
734    public String forDeleteEntityBatchByIds(TableInfo tableInfo, Object[] primaryValues) {
735        String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip();
736        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
737
738        // 正常删除
739        if (StringUtil.noText(logicDeleteColumn)) {
740            String deleteSQL = forDeleteBatchByIds(tableInfo.getSchema(), tableInfo.getTableName(), tableInfo.getPrimaryColumns(), primaryValues);
741
742            // 多租户
743            if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
744                deleteSQL = deleteSQL.replace(WHERE, WHERE + BRACKET_LEFT) + BRACKET_RIGHT;
745                deleteSQL = tableInfo.buildTenantCondition(deleteSQL, tenantIdArgs, this);
746            }
747            return deleteSQL;
748        }
749
750        StringBuilder sql = new StringBuilder();
751        sql.append(UPDATE);
752        sql.append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE));
753        sql.append(SET).append(buildLogicDeletedSet(logicDeleteColumn, tableInfo));
754        sql.append(WHERE);
755        sql.append(BRACKET_LEFT);
756
757        String[] primaryKeys = tableInfo.getPrimaryColumns();
758
759        // 多主键的场景
760        if (primaryKeys.length > 1) {
761            for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) {
762                if (i > 0) {
763                    sql.append(OR);
764                }
765                sql.append(BRACKET_LEFT);
766                for (int j = 0; j < primaryKeys.length; j++) {
767                    if (j > 0) {
768                        sql.append(AND);
769                    }
770                    sql.append(wrap(primaryKeys[j])).append(EQUALS_PLACEHOLDER);
771                }
772                sql.append(BRACKET_RIGHT);
773            }
774        }
775        // 单主键
776        else {
777            for (int i = 0; i < primaryValues.length; i++) {
778                if (i > 0) {
779                    sql.append(OR);
780                }
781                sql.append(wrap(primaryKeys[0])).append(EQUALS_PLACEHOLDER);
782            }
783        }
784
785        sql.append(BRACKET_RIGHT).append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo));
786
787        tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
788        prepareAuth(tableInfo, sql, OperateType.DELETE);
789        return sql.toString();
790    }
791
792    @Override
793    public String forDeleteEntityBatchByQuery(TableInfo tableInfo, QueryWrapper queryWrapper) {
794
795        String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip();
796
797        // 正常删除
798        if (StringUtil.noText(logicDeleteColumn)) {
799            return forDeleteByQuery(queryWrapper);
800        }
801
802
803        prepareAuth(queryWrapper, OperateType.DELETE);
804        // 逻辑删除
805        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
806        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
807        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
808
809        // ignore selectColumns
810        StringBuilder sqlBuilder = new StringBuilder(UPDATE).append(forHint(CPI.getHint(queryWrapper)));
811        sqlBuilder.append(tableInfo.getWrapSchemaAndTableName(this, OperateType.DELETE));
812        sqlBuilder.append(SET).append(buildLogicDeletedSet(logicDeleteColumn, tableInfo));
813
814
815        buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.DELETE);
816        buildWhereSql(sqlBuilder, queryWrapper, allTables, false);
817        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
818        buildHavingSql(sqlBuilder, queryWrapper, allTables);
819
820        // ignore orderBy and limit
821        // buildOrderBySql(sqlBuilder, queryWrapper)
822        // buildLimitSql(sqlBuilder, queryWrapper)
823
824        return sqlBuilder.toString();
825    }
826
827
828    @Override
829    public String forUpdateEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
830        StringBuilder sql = new StringBuilder();
831
832        Set<String> updateColumns = tableInfo.obtainUpdateColumns(entity, ignoreNulls, false);
833        Map<String, RawValue> rawValueMap = tableInfo.obtainUpdateRawValueMap(entity);
834        String[] primaryKeys = tableInfo.getPrimaryColumns();
835
836        sql.append(UPDATE).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE)).append(SET);
837
838        StringJoiner stringJoiner = new StringJoiner(DELIMITER);
839
840        for (String updateColumn : updateColumns) {
841            if (rawValueMap.containsKey(updateColumn)) {
842                stringJoiner.add(wrap(updateColumn) + EQUALS + rawValueMap.get(updateColumn).toSql(this));
843            } else {
844                stringJoiner.add(wrap(updateColumn) + EQUALS_PLACEHOLDER);
845            }
846        }
847
848        Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns();
849        if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) {
850            onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + EQUALS + value));
851        }
852
853        // 乐观锁字段
854        String versionColumn = tableInfo.getVersionColumn();
855        if (StringUtil.hasText(tableInfo.getOptimisticLockColumnOrSkip())) {
856            stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
857        }
858
859        sql.append(stringJoiner);
860
861        sql.append(WHERE);
862        for (int i = 0; i < primaryKeys.length; i++) {
863            if (i > 0) {
864                sql.append(AND);
865            }
866            sql.append(wrap(primaryKeys[i])).append(EQUALS_PLACEHOLDER);
867        }
868
869        // 逻辑删除条件,已删除的数据不能被修改
870        String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip();
871        if (StringUtil.hasText(logicDeleteColumn)) {
872            sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo));
873        }
874
875
876        // 租户ID字段
877        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
878        tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
879
880        // 乐观锁条件
881        if (StringUtil.hasText(tableInfo.getOptimisticLockColumnOrSkip())) {
882            Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn);
883            if (versionValue == null) {
884                throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);
885            }
886            sql.append(AND).append(wrap(versionColumn)).append(EQUALS).append(versionValue);
887        }
888
889        prepareAuth(tableInfo, sql, OperateType.UPDATE);
890        return sql.toString();
891    }
892
893    @Override
894    public String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper) {
895        prepareAuth(queryWrapper, OperateType.UPDATE);
896        StringBuilder sqlBuilder = new StringBuilder();
897
898        Set<String> updateColumns = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true);
899        Map<String, RawValue> rawValueMap = tableInfo.obtainUpdateRawValueMap(entity);
900
901        sqlBuilder.append(UPDATE).append(forHint(CPI.getHint(queryWrapper)));
902        sqlBuilder.append(tableInfo.getWrapSchemaAndTableName(this, OperateType.UPDATE));
903
904        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
905        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
906        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
907        buildJoinSql(sqlBuilder, queryWrapper, allTables, OperateType.UPDATE);
908
909
910        sqlBuilder.append(SET);
911
912        StringJoiner stringJoiner = new StringJoiner(DELIMITER);
913
914        for (String modifyAttr : updateColumns) {
915            if (rawValueMap.containsKey(modifyAttr)) {
916                stringJoiner.add(wrap(modifyAttr) + EQUALS + rawValueMap.get(modifyAttr).toSql(this));
917            } else {
918                stringJoiner.add(wrap(modifyAttr) + EQUALS_PLACEHOLDER);
919            }
920        }
921
922
923        Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns();
924        if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) {
925            onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + EQUALS + value));
926        }
927
928        // 乐观锁字段
929        String versionColumn = tableInfo.getVersionColumn();
930        if (StringUtil.hasText(tableInfo.getOptimisticLockColumnOrSkip())) {
931            stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
932        }
933
934        sqlBuilder.append(stringJoiner);
935
936
937        buildWhereSql(sqlBuilder, queryWrapper, queryTables, false);
938        buildGroupBySql(sqlBuilder, queryWrapper, queryTables);
939        buildHavingSql(sqlBuilder, queryWrapper, queryTables);
940
941        // ignore orderBy and limit
942        buildOrderBySql(sqlBuilder, queryWrapper, queryTables);
943
944        Long limitRows = CPI.getLimitRows(queryWrapper);
945        Long limitOffset = CPI.getLimitOffset(queryWrapper);
946        if (limitRows != null || limitOffset != null) {
947            sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
948        }
949
950
951        List<String> endFragments = CPI.getEndFragments(queryWrapper);
952        if (CollectionUtil.isNotEmpty(endFragments)) {
953            for (String endFragment : endFragments) {
954                sqlBuilder.append(BLANK).append(endFragment);
955            }
956        }
957
958        return sqlBuilder.toString();
959    }
960
961
962    @Override
963    public String forSelectOneEntityById(TableInfo tableInfo) {
964        StringBuilder sql = new StringBuilder();
965        buildSelectColumnSql(sql, null, null, null);
966        sql.append(FROM).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.SELECT));
967        sql.append(WHERE);
968        String[] pKeys = tableInfo.getPrimaryColumns();
969        for (int i = 0; i < pKeys.length; i++) {
970            if (i > 0) {
971                sql.append(AND);
972            }
973            sql.append(wrap(pKeys[i])).append(EQUALS_PLACEHOLDER);
974        }
975
976        // 逻辑删除的情况下,需要添加逻辑删除的条件
977        String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip();
978        if (StringUtil.hasText(logicDeleteColumn)) {
979            sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo));
980        }
981
982        // 多租户
983        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
984        tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
985        prepareAuth(tableInfo, sql, OperateType.SELECT);
986        return sql.toString();
987    }
988
989
990    @Override
991    public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) {
992        StringBuilder sql = new StringBuilder();
993        buildSelectColumnSql(sql, null, tableInfo.getDefaultQueryColumn(), null);
994        sql.append(FROM).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.SELECT));
995        sql.append(WHERE);
996        String[] primaryKeys = tableInfo.getPrimaryColumns();
997
998        String logicDeleteColumn = tableInfo.getLogicDeleteColumnOrSkip();
999        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
1000        if (StringUtil.hasText(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) {
1001            sql.append(BRACKET_LEFT);
1002        }
1003
1004        // 多主键的场景
1005        if (primaryKeys.length > 1) {
1006            for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) {
1007                if (i > 0) {
1008                    sql.append(OR);
1009                }
1010                sql.append(BRACKET_LEFT);
1011                for (int j = 0; j < primaryKeys.length; j++) {
1012                    if (j > 0) {
1013                        sql.append(AND);
1014                    }
1015                    sql.append(wrap(primaryKeys[j])).append(EQUALS_PLACEHOLDER);
1016                }
1017                sql.append(BRACKET_RIGHT);
1018            }
1019        }
1020        // 单主键
1021        else {
1022            for (int i = 0; i < primaryValues.length; i++) {
1023                if (i > 0) {
1024                    sql.append(OR);
1025                }
1026                sql.append(wrap(primaryKeys[0])).append(EQUALS_PLACEHOLDER);
1027            }
1028        }
1029
1030        if (StringUtil.hasText(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) {
1031            sql.append(BRACKET_RIGHT);
1032        }
1033
1034
1035        if (StringUtil.hasText(logicDeleteColumn)) {
1036            sql.append(AND).append(buildLogicNormalCondition(logicDeleteColumn, tableInfo));
1037        }
1038
1039        // 多租户
1040        tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
1041        prepareAuth(tableInfo, sql, OperateType.SELECT);
1042        return sql.toString();
1043    }
1044
1045
1046    protected boolean buildJoinSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables, OperateType operateType) {
1047        List<Join> joins = CPI.getJoins(queryWrapper);
1048        boolean joinSuccess = false;
1049        if (joins != null && !joins.isEmpty()) {
1050            for (Join join : joins) {
1051                if (!join.checkEffective()) {
1052                    continue;
1053                }
1054                sqlBuilder.append(join.toSql(queryTables, this, operateType));
1055                joinSuccess = true;
1056            }
1057        }
1058        return joinSuccess;
1059    }
1060
1061
1062    protected void buildWhereSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables, boolean allowNoCondition) {
1063        QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
1064        if (whereQueryCondition != null) {
1065            String whereSql = whereQueryCondition.toSql(queryTables, this);
1066            if (StringUtil.hasText(whereSql)) {
1067                sqlBuilder.append(WHERE).append(whereSql);
1068            } else if (!allowNoCondition) {
1069                throw FlexExceptions.wrap(LocalizedFormats.UPDATE_OR_DELETE_NOT_ALLOW);
1070            }
1071        } else {
1072            // whereQueryCondition == null
1073            if (!allowNoCondition) {
1074                throw FlexExceptions.wrap(LocalizedFormats.UPDATE_OR_DELETE_NOT_ALLOW);
1075            }
1076        }
1077    }
1078
1079
1080    protected void buildGroupBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
1081        List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
1082        if (groupByColumns != null && !groupByColumns.isEmpty()) {
1083            sqlBuilder.append(GROUP_BY);
1084            int index = 0;
1085            for (QueryColumn groupByColumn : groupByColumns) {
1086                String groupBy = CPI.toConditionSql(groupByColumn, queryTables, this);
1087                sqlBuilder.append(groupBy);
1088                if (index != groupByColumns.size() - 1) {
1089                    sqlBuilder.append(DELIMITER);
1090                }
1091                index++;
1092            }
1093        }
1094    }
1095
1096
1097    protected void buildHavingSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
1098        QueryCondition havingQueryCondition = CPI.getHavingQueryCondition(queryWrapper);
1099        if (havingQueryCondition != null) {
1100            String havingSql = havingQueryCondition.toSql(queryTables, this);
1101            if (StringUtil.hasText(havingSql)) {
1102                sqlBuilder.append(HAVING).append(havingSql);
1103            }
1104        }
1105    }
1106
1107
1108    protected void buildOrderBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
1109        List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
1110        if (orderBys != null && !orderBys.isEmpty()) {
1111            sqlBuilder.append(ORDER_BY);
1112            int index = 0;
1113            for (QueryOrderBy orderBy : orderBys) {
1114                sqlBuilder.append(orderBy.toSql(queryTables, this));
1115                if (index != orderBys.size() - 1) {
1116                    sqlBuilder.append(DELIMITER);
1117                }
1118                index++;
1119            }
1120        }
1121    }
1122
1123
1124    /**
1125     * 构建 limit 和 offset 的参数
1126     */
1127    protected StringBuilder buildLimitOffsetSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, Long limitRows, Long limitOffset) {
1128        return limitOffsetProcessor.process(this, sqlBuilder, queryWrapper, limitRows, limitOffset);
1129    }
1130
1131
1132    protected String buildLogicNormalCondition(String logicColumn, TableInfo tableInfo) {
1133        return LogicDeleteManager.getProcessor().buildLogicNormalCondition(logicColumn, tableInfo, this);
1134    }
1135
1136
1137    protected String buildLogicDeletedSet(String logicColumn, TableInfo tableInfo) {
1138        return LogicDeleteManager.getProcessor().buildLogicDeletedSet(logicColumn, tableInfo, this);
1139    }
1140
1141
1142}