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