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