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;
017
018import com.mybatisflex.core.FlexGlobalConfig;
019import com.mybatisflex.core.exception.FlexExceptions;
020import com.mybatisflex.core.query.*;
021import com.mybatisflex.core.row.Row;
022import com.mybatisflex.core.row.RowCPI;
023import com.mybatisflex.core.table.TableInfo;
024import com.mybatisflex.core.util.ArrayUtil;
025import com.mybatisflex.core.util.CollectionUtil;
026import com.mybatisflex.core.util.StringUtil;
027
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031import java.util.StringJoiner;
032
033/**
034 * 通用的方言设计,其他方言可以继承于当前 CommonsDialectImpl
035 * 创建或获取方言请参考 {@link com.mybatisflex.core.dialect.DialectFactory}
036 */
037public class CommonsDialectImpl implements IDialect {
038
039    protected KeywordWrap keywordWrap = KeywordWrap.BACKQUOTE;
040    private LimitOffsetProcesser limitOffsetProcesser = LimitOffsetProcesser.MYSQL;
041
042    public CommonsDialectImpl() {
043    }
044
045    public CommonsDialectImpl(LimitOffsetProcesser limitOffsetProcesser) {
046        this.limitOffsetProcesser = limitOffsetProcesser;
047    }
048
049    public CommonsDialectImpl(KeywordWrap keywordWrap, LimitOffsetProcesser limitOffsetProcesser) {
050        this.keywordWrap = keywordWrap;
051        this.limitOffsetProcesser = limitOffsetProcesser;
052    }
053
054    @Override
055    public String wrap(String keyword) {
056        return keywordWrap.wrap(keyword);
057    }
058
059    @Override
060    public String forInsertRow(String tableName, Row row) {
061        StringBuilder fields = new StringBuilder();
062        StringBuilder questions = new StringBuilder();
063
064        Set<String> attrs = row.obtainModifyAttrs();
065        int index = 0;
066        for (String attr : attrs) {
067            fields.append(wrap(attr));
068            questions.append("?");
069            if (index != attrs.size() - 1) {
070                fields.append(", ");
071                questions.append(", ");
072            }
073            index++;
074        }
075
076        String sql = "INSERT INTO " + wrap(tableName) +
077                "(" + fields + ")  VALUES " +
078                "(" + questions + ")";
079        return sql;
080    }
081
082
083    @Override
084    public String forInsertBatchWithFirstRowColumns(String tableName, List<Row> rows) {
085        StringBuilder fields = new StringBuilder();
086        StringBuilder questions = new StringBuilder();
087
088        Row firstRow = rows.get(0);
089        Set<String> attrs = firstRow.obtainModifyAttrs();
090        int index = 0;
091        for (String attr : attrs) {
092            fields.append(wrap(attr));
093            if (index != attrs.size() - 1) {
094                fields.append(", ");
095            }
096            index++;
097        }
098
099        for (int i = 0; i < rows.size(); i++) {
100            questions.append(buildQuestion(attrs.size(), true));
101            if (i != rows.size() - 1) {
102                questions.append(",");
103            }
104        }
105
106        String sql = "INSERT INTO " + wrap(tableName) +
107                "(" + fields + ")  VALUES " + questions;
108        return sql;
109    }
110
111
112    @Override
113    public String forDeleteById(String tableName, String[] primaryKeys) {
114        StringBuilder sql = new StringBuilder();
115        sql.append("DELETE FROM ");
116        sql.append(wrap(tableName));
117        sql.append(" WHERE ");
118        for (int i = 0; i < primaryKeys.length; i++) {
119            if (i > 0) {
120                sql.append(" AND ");
121            }
122            sql.append(wrap(primaryKeys[i])).append(" = ?");
123        }
124        return sql.toString();
125    }
126
127
128    @Override
129    public String forDeleteBatchByIds(String tableName, String[] primaryKeys, Object[] ids) {
130        StringBuilder sql = new StringBuilder();
131        sql.append("DELETE FROM ");
132        sql.append(wrap(tableName));
133        sql.append(" WHERE ");
134
135        //多主键的场景
136        if (primaryKeys.length > 1) {
137            for (int i = 0; i < ids.length / primaryKeys.length; i++) {
138                if (i > 0) {
139                    sql.append(" OR ");
140                }
141                sql.append("(");
142                for (int j = 0; j < primaryKeys.length; j++) {
143                    if (j > 0) {
144                        sql.append(" AND ");
145                    }
146                    sql.append(wrap(primaryKeys[j])).append(" = ?");
147                }
148                sql.append(")");
149            }
150        }
151        // 单主键
152        else {
153            for (int i = 0; i < ids.length; i++) {
154                if (i > 0) {
155                    sql.append(" OR ");
156                }
157                sql.append(wrap(primaryKeys[0])).append(" = ?");
158            }
159        }
160        return sql.toString();
161    }
162
163    @Override
164    public String forDeleteByQuery(QueryWrapper queryWrapper) {
165        return buildDeleteSql(queryWrapper);
166    }
167
168    @Override
169    public String forUpdateById(String tableName, Row row) {
170        StringBuilder sql = new StringBuilder();
171
172        Set<String> modifyAttrs = row.obtainModifyAttrs();
173        String[] primaryKeys = RowCPI.obtainsPrimaryKeyStrings(row);
174
175        sql.append("UPDATE ").append(wrap(tableName)).append(" SET ");
176        int index = 0;
177        for (Map.Entry<String, Object> e : row.entrySet()) {
178            String colName = e.getKey();
179            if (modifyAttrs.contains(colName) && !ArrayUtil.contains(primaryKeys, colName)) {
180                if (index > 0) {
181                    sql.append(", ");
182                }
183                sql.append(wrap(colName)).append(" = ? ");
184                index++;
185            }
186        }
187        sql.append(" WHERE ");
188        for (int i = 0; i < primaryKeys.length; i++) {
189            if (i > 0) {
190                sql.append(" AND ");
191            }
192            sql.append(wrap(primaryKeys[i])).append(" = ?");
193        }
194
195        return sql.toString();
196    }
197
198    @Override
199    public String forUpdateByQuery(QueryWrapper queryWrapper, Row row) {
200        StringBuilder sql = new StringBuilder();
201
202        Set<String> modifyAttrs = row.obtainModifyAttrs();
203
204        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
205        if (queryTables == null || queryTables.size() != 1) {
206            throw FlexExceptions.wrap("update sql must need 1 table.");
207        }
208
209        String tableName = queryTables.get(0).getName();
210        sql.append("UPDATE ").append(wrap(tableName)).append(" SET ");
211        int index = 0;
212        for (String modifyAttr : modifyAttrs) {
213            if (index > 0) {
214                sql.append(", ");
215            }
216            sql.append(wrap(modifyAttr)).append(" = ? ");
217            index++;
218        }
219
220        String whereConditionSql = buildWhereConditionSql(queryWrapper);
221        if (StringUtil.isNotBlank(whereConditionSql)) {
222            sql.append(" WHERE ").append(whereConditionSql);
223        }
224
225        return sql.toString();
226    }
227
228    @Override
229    public String forUpdateBatchById(String tableName, List<Row> rows) {
230        if (rows.size() == 1) {
231            return forUpdateById(tableName, rows.get(0));
232        }
233        StringBuilder sql = new StringBuilder();
234        for (Row row : rows) {
235            sql.append(forUpdateById(tableName, row)).append("; ");
236        }
237        return sql.toString();
238    }
239
240
241    @Override
242    public String forSelectOneById(String tableName, String[] primaryKeys, Object[] primaryValues) {
243        StringBuilder sql = new StringBuilder("SELECT * FROM ");
244        sql.append(wrap(tableName)).append(" WHERE ");
245        for (int i = 0; i < primaryKeys.length; i++) {
246            if (i > 0) {
247                sql.append(" AND ");
248            }
249            sql.append(wrap(primaryKeys[i])).append(" = ?");
250        }
251        return sql.toString();
252    }
253
254    @Override
255    public String forSelectListByQuery(QueryWrapper queryWrapper) {
256        return buildSelectSql(queryWrapper);
257    }
258
259
260    @Override
261    public String forSelectCountByQuery(QueryWrapper queryWrapper) {
262        return buildSelectCountSql(queryWrapper);
263    }
264
265
266    ////////////build query sql///////
267    @Override
268    public String buildSelectSql(QueryWrapper queryWrapper) {
269        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
270        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
271        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
272
273        List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
274
275        StringBuilder sqlBuilder = new StringBuilder("SELECT ");
276        if (selectColumns == null || selectColumns.isEmpty()) {
277            sqlBuilder.append("*");
278        } else {
279            int index = 0;
280
281            for (QueryColumn selectColumn : selectColumns) {
282                String selectColumnSql = CPI.toSelectSql(selectColumn, allTables, this);
283                sqlBuilder.append(selectColumnSql);
284                if (index != selectColumns.size() - 1) {
285                    sqlBuilder.append(", ");
286                }
287                index++;
288            }
289        }
290        sqlBuilder.append(" FROM ").append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
291
292        buildJoinSql(sqlBuilder, queryWrapper, allTables);
293        buildWhereSql(sqlBuilder, queryWrapper, allTables, true);
294        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
295        buildHavingSql(sqlBuilder, queryWrapper, allTables);
296        buildOrderBySql(sqlBuilder, queryWrapper, allTables);
297
298        List<UnionWrapper> unions = CPI.getUnions(queryWrapper);
299        if (CollectionUtil.isNotEmpty(unions)) {
300            sqlBuilder.insert(0, "(").append(")");
301            for (UnionWrapper unionWrapper : unions) {
302                unionWrapper.buildSql(sqlBuilder, this);
303            }
304        }
305
306        Integer limitRows = CPI.getLimitRows(queryWrapper);
307        Integer limitOffset = CPI.getLimitOffset(queryWrapper);
308        if (limitRows != null || limitOffset != null) {
309            sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
310        }
311
312        return sqlBuilder.toString();
313    }
314
315
316    @Override
317    public String buildSelectCountSql(QueryWrapper queryWrapper) {
318        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
319        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
320        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
321
322        //ignore selectColumns
323        StringBuilder sqlBuilder = new StringBuilder("SELECT COUNT(*) FROM ");
324        sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
325
326
327        buildJoinSql(sqlBuilder, queryWrapper, allTables);
328        buildWhereSql(sqlBuilder, queryWrapper, allTables, true);
329        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
330        buildHavingSql(sqlBuilder, queryWrapper, allTables);
331
332        // ignore orderBy and limit
333        // buildOrderBySql(sqlBuilder, queryWrapper);
334        // buildLimitSql(sqlBuilder, queryWrapper);
335
336        return sqlBuilder.toString();
337    }
338
339    @Override
340    public String buildDeleteSql(QueryWrapper queryWrapper) {
341        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
342        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
343        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
344
345        //ignore selectColumns
346        StringBuilder sqlBuilder = new StringBuilder("DELETE FROM ");
347        sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
348
349        buildJoinSql(sqlBuilder, queryWrapper, allTables);
350        buildWhereSql(sqlBuilder, queryWrapper, allTables, false);
351        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
352        buildHavingSql(sqlBuilder, queryWrapper, allTables);
353
354        //ignore orderBy and limit
355        //buildOrderBySql(sqlBuilder, queryWrapper);
356        //buildLimitSql(sqlBuilder, queryWrapper);
357
358        return sqlBuilder.toString();
359    }
360
361    @Override
362    public String buildWhereConditionSql(QueryWrapper queryWrapper) {
363        QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
364        return whereQueryCondition != null ? whereQueryCondition.toSql(CPI.getQueryTables(queryWrapper), this) : "";
365    }
366
367    @Override
368    public String forInsertEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
369        StringBuilder sql = new StringBuilder();
370        sql.append("INSERT INTO ").append(wrap(tableInfo.getTableName()));
371
372        String[] insertColumns = tableInfo.obtainInsertColumns(entity, ignoreNulls);
373        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
374
375        StringJoiner sqlFields = new StringJoiner(", ");
376        StringJoiner sqlValues = new StringJoiner(", ");
377
378        for (String insertColumn : insertColumns) {
379            sqlFields.add(wrap(insertColumn));
380            if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
381                sqlValues.add(onInsertColumns.get(insertColumn));
382            } else {
383                sqlValues.add("?");
384            }
385        }
386
387        return sql.append("(").append(sqlFields).append(")")
388                .append(" VALUES ")
389                .append("(").append(sqlValues).append(")").toString();
390    }
391
392    @Override
393    public String forInsertEntityBatch(TableInfo tableInfo, List<Object> entities) {
394        StringBuilder sql = new StringBuilder();
395        sql.append("INSERT INTO ").append(wrap(tableInfo.getTableName()));
396        String[] insertColumns = tableInfo.obtainInsertColumns(null, false);
397        String[] warpedInsertColumns = new String[insertColumns.length];
398        for (int i = 0; i < insertColumns.length; i++) {
399            warpedInsertColumns[i] = wrap(insertColumns[i]);
400        }
401        sql.append("(").append(StringUtil.join(", ", warpedInsertColumns)).append(")");
402        sql.append(" VALUES ");
403
404        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
405        for (int i = 0; i < entities.size(); i++) {
406            StringJoiner stringJoiner = new StringJoiner(", ", "(", ")");
407            for (String insertColumn : warpedInsertColumns) {
408                if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
409                    //直接读取 onInsert 配置的值,而不用 "?" 代替
410                    stringJoiner.add(onInsertColumns.get(insertColumn));
411                } else {
412                    stringJoiner.add("?");
413                }
414            }
415            sql.append(stringJoiner);
416            if (i != entities.size() - 1) {
417                sql.append(", ");
418            }
419        }
420
421        return sql.toString();
422    }
423
424    @Override
425    public String forDeleteEntityById(TableInfo tableInfo) {
426        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
427        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
428        //正常删除
429        if (StringUtil.isBlank(logicDeleteColumn)) {
430            String deleteByIdSql = forDeleteById(tableInfo.getTableName(), tableInfo.getPrimaryKeys());
431
432            if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
433                deleteByIdSql += " AND " + wrap(tableInfo.getTenantIdColumn()) + " IN " + buildQuestion(tenantIdArgs.length, true);
434            }
435            return deleteByIdSql;
436        }
437
438        //逻辑删除
439        StringBuilder sql = new StringBuilder();
440        String[] primaryKeys = tableInfo.getPrimaryKeys();
441
442        sql.append("UPDATE ").append(wrap(tableInfo.getTableName()));
443        sql.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
444        sql.append(" WHERE ");
445        for (int i = 0; i < primaryKeys.length; i++) {
446            if (i > 0) {
447                sql.append(" AND ");
448            }
449            sql.append(wrap(primaryKeys[i])).append(" = ?");
450        }
451
452        sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
453
454        //租户ID
455        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
456            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
457        }
458
459        return sql.toString();
460    }
461
462
463    @Override
464    public String forDeleteEntityBatchByIds(TableInfo tableInfo, Object[] primaryValues) {
465        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
466        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
467
468        //正常删除
469        if (StringUtil.isBlank(logicDeleteColumn)) {
470            String deleteSQL = forDeleteBatchByIds(tableInfo.getTableName(), tableInfo.getPrimaryKeys(), primaryValues);
471
472            //多租户
473            if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
474                deleteSQL = deleteSQL.replace(" WHERE ", " WHERE (") + ")";
475                deleteSQL += " AND " + wrap(tableInfo.getTenantIdColumn()) + " IN " + buildQuestion(tenantIdArgs.length, true);
476            }
477            return deleteSQL;
478        }
479
480        StringBuilder sql = new StringBuilder();
481        sql.append("UPDATE ");
482        sql.append(wrap(tableInfo.getTableName()));
483        sql.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
484        sql.append(" WHERE ");
485        sql.append("(");
486
487        String[] primaryKeys = tableInfo.getPrimaryKeys();
488
489        //多主键的场景
490        if (primaryKeys.length > 1) {
491            for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) {
492                if (i > 0) {
493                    sql.append(" OR ");
494                }
495                sql.append("(");
496                for (int j = 0; j < primaryKeys.length; j++) {
497                    if (j > 0) {
498                        sql.append(" AND ");
499                    }
500                    sql.append(wrap(primaryKeys[j])).append(" = ?");
501                }
502                sql.append(")");
503            }
504        }
505        // 单主键
506        else {
507            for (int i = 0; i < primaryValues.length; i++) {
508                if (i > 0) {
509                    sql.append(" OR ");
510                }
511                sql.append(wrap(primaryKeys[0])).append(" = ?");
512            }
513        }
514
515        sql.append(") AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
516
517        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
518            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
519        }
520
521        return sql.toString();
522    }
523
524    @Override
525    public String forDeleteEntityBatchByQuery(TableInfo tableInfo, QueryWrapper queryWrapper) {
526
527        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
528
529        //正常删除
530        if (StringUtil.isBlank(logicDeleteColumn)) {
531            return forDeleteByQuery(queryWrapper);
532        }
533
534
535        //逻辑删除
536        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
537        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
538        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
539
540        //ignore selectColumns
541        StringBuilder sqlBuilder = new StringBuilder("UPDATE ");
542        sqlBuilder.append(wrap(tableInfo.getTableName()));
543        sqlBuilder.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
544
545
546        buildJoinSql(sqlBuilder, queryWrapper, allTables);
547        buildWhereSql(sqlBuilder, queryWrapper, allTables, false);
548        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
549        buildHavingSql(sqlBuilder, queryWrapper, allTables);
550
551        //ignore orderBy and limit
552        //buildOrderBySql(sqlBuilder, queryWrapper);
553        //buildLimitSql(sqlBuilder, queryWrapper);
554
555        return sqlBuilder.toString();
556    }
557
558
559    @Override
560    public String forUpdateEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
561        StringBuilder sql = new StringBuilder();
562
563        Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, false);
564        String[] primaryKeys = tableInfo.getPrimaryKeys();
565
566        sql.append("UPDATE ").append(wrap(tableInfo.getTableName())).append(" SET ");
567
568        StringJoiner stringJoiner = new StringJoiner(", ");
569
570        for (String modifyAttr : modifyAttrs) {
571            stringJoiner.add(wrap(modifyAttr) + " = ?");
572        }
573
574        Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns();
575        if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) {
576            onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + " = " + value));
577        }
578
579        //乐观锁字段
580        String versionColumn = tableInfo.getVersionColumn();
581        if (StringUtil.isNotBlank(versionColumn)) {
582            stringJoiner.add(wrap(versionColumn) + " = " + wrap(versionColumn) + " + 1 ");
583        }
584
585        sql.append(stringJoiner);
586
587        sql.append(" WHERE ");
588        for (int i = 0; i < primaryKeys.length; i++) {
589            if (i > 0) {
590                sql.append(" AND ");
591            }
592            sql.append(wrap(primaryKeys[i])).append(" = ?");
593        }
594
595        //逻辑删除条件,已删除的数据不能被修改
596        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
597        if (StringUtil.isNotBlank(logicDeleteColumn)) {
598            sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
599        }
600
601
602        //租户ID字段
603        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
604        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
605            if (tenantIdArgs.length == 1) {
606                sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" = ?");
607            } else {
608                sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
609            }
610        }
611
612        //乐观锁条件
613        if (StringUtil.isNotBlank(versionColumn)) {
614            Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn);
615            if (versionValue == null) {
616                throw FlexExceptions.wrap("The version value of entity[%s] must not be null.", entity);
617            }
618            sql.append(" AND ").append(wrap(versionColumn)).append(" = ").append(versionValue);
619        }
620
621
622        return sql.toString();
623    }
624
625    @Override
626    public String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper) {
627        StringBuilder sql = new StringBuilder();
628
629        Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true);
630
631        sql.append("UPDATE ").append(wrap(tableInfo.getTableName())).append(" SET ");
632
633        StringJoiner stringJoiner = new StringJoiner(", ");
634
635        for (String modifyAttr : modifyAttrs) {
636            stringJoiner.add(wrap(modifyAttr) + " = ?");
637        }
638
639        Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns();
640        if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) {
641            onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + " = " + value));
642        }
643
644        //乐观锁字段
645        String versionColumn = tableInfo.getVersionColumn();
646        if (StringUtil.isNotBlank(versionColumn)) {
647            stringJoiner.add(wrap(versionColumn) + " = " + wrap(versionColumn) + " + 1 ");
648        }
649
650        sql.append(stringJoiner);
651
652
653        String whereConditionSql = buildWhereConditionSql(queryWrapper);
654
655        //不允许全量更新
656        if (StringUtil.isBlank(whereConditionSql)) {
657            throw new IllegalArgumentException("Not allowed UPDATE a table without where condition.");
658        }
659
660        sql.append(" WHERE ").append(whereConditionSql);
661        return sql.toString();
662    }
663
664    @Override
665    public String forSelectOneEntityById(TableInfo tableInfo) {
666        StringBuilder sql = new StringBuilder("SELECT * FROM ");
667        sql.append(wrap(tableInfo.getTableName()));
668        sql.append(" WHERE ");
669        String[] pKeys = tableInfo.getPrimaryKeys();
670        for (int i = 0; i < pKeys.length; i++) {
671            if (i > 0) {
672                sql.append(" AND ");
673            }
674            sql.append(wrap(pKeys[i])).append(" = ?");
675        }
676
677        //逻辑删除的情况下,需要添加逻辑删除的条件
678        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
679        if (StringUtil.isNotBlank(logicDeleteColumn)) {
680            sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
681        }
682
683        //多租户
684        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
685        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
686            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
687        }
688
689        return sql.toString();
690    }
691
692
693    @Override
694    public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) {
695        StringBuilder sql = new StringBuilder("SELECT * FROM ");
696        sql.append(wrap(tableInfo.getTableName()));
697        sql.append(" WHERE ");
698        String[] primaryKeys = tableInfo.getPrimaryKeys();
699
700        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
701        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
702        if (StringUtil.isNotBlank(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) {
703            sql.append("(");
704        }
705
706        //多主键的场景
707        if (primaryKeys.length > 1) {
708            for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) {
709                if (i > 0) {
710                    sql.append(" OR ");
711                }
712                sql.append("(");
713                for (int j = 0; j < primaryKeys.length; j++) {
714                    if (j > 0) {
715                        sql.append(" AND ");
716                    }
717                    sql.append(wrap(primaryKeys[j])).append(" = ?");
718                }
719                sql.append(")");
720            }
721        }
722        // 单主键
723        else {
724            for (int i = 0; i < primaryValues.length; i++) {
725                if (i > 0) {
726                    sql.append(" OR ");
727                }
728                sql.append(wrap(primaryKeys[0])).append(" = ?");
729            }
730        }
731
732        if (StringUtil.isNotBlank(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) {
733            sql.append(")");
734        }
735
736
737        if (StringUtil.isNotBlank(logicDeleteColumn)) {
738            sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
739        }
740
741        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
742            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN").append(buildQuestion(tenantIdArgs.length, true));
743        }
744
745        return sql.toString();
746    }
747
748
749    protected void buildJoinSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
750        List<Join> joins = CPI.getJoins(queryWrapper);
751        if (joins != null && !joins.isEmpty()) {
752            for (Join join : joins) {
753                if (!join.checkEffective()) {
754                    continue;
755                }
756                sqlBuilder.append(join.toSql(queryTables, this));
757            }
758        }
759    }
760
761
762    protected void buildWhereSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables, boolean allowNoCondition) {
763        QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
764        if (whereQueryCondition != null) {
765            String whereSql = whereQueryCondition.toSql(queryTables, this);
766            if (StringUtil.isNotBlank(whereSql)) {
767                sqlBuilder.append(" WHERE ").append(whereSql);
768            } else if (!allowNoCondition) {
769                throw new IllegalArgumentException("Not allowed DELETE a table without where condition.");
770            }
771        }
772    }
773
774
775    protected void buildGroupBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
776        List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
777        if (groupByColumns != null && !groupByColumns.isEmpty()) {
778            sqlBuilder.append(" GROUP BY ");
779            int index = 0;
780            for (QueryColumn groupByColumn : groupByColumns) {
781                String groupBy = CPI.toConditionSql(groupByColumn, queryTables, this);
782                sqlBuilder.append(groupBy);
783                if (index != groupByColumns.size() - 1) {
784                    sqlBuilder.append(", ");
785                }
786                index++;
787            }
788        }
789    }
790
791
792    protected void buildHavingSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
793        QueryCondition havingQueryCondition = CPI.getHavingQueryCondition(queryWrapper);
794        if (havingQueryCondition != null) {
795            String havingSql = havingQueryCondition.toSql(queryTables, this);
796            if (StringUtil.isNotBlank(havingSql)) {
797                sqlBuilder.append(" HAVING ").append(havingSql);
798            }
799        }
800    }
801
802
803    protected void buildOrderBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
804        List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
805        if (orderBys != null && !orderBys.isEmpty()) {
806            sqlBuilder.append(" ORDER BY ");
807            int index = 0;
808            for (QueryOrderBy orderBy : orderBys) {
809                sqlBuilder.append(orderBy.toSql(queryTables, this));
810                if (index != orderBys.size() - 1) {
811                    sqlBuilder.append(", ");
812                }
813                index++;
814            }
815        }
816    }
817
818
819    /**
820     * 构建 limit 和 offset 的参数
821     */
822    protected StringBuilder buildLimitOffsetSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, Integer limitRows, Integer limitOffset) {
823        return limitOffsetProcesser.process(sqlBuilder, queryWrapper, limitRows, limitOffset);
824    }
825
826
827    protected String buildQuestion(int count, boolean withBrackets) {
828        StringBuilder sb = new StringBuilder();
829        for (int i = 0; i < count; i++) {
830            sb.append("?");
831            if (i != count - 1) {
832                sb.append(", ");
833            }
834        }
835        return withBrackets ? "(" + sb + ")" : sb.toString();
836    }
837
838
839    protected Object getLogicNormalValue() {
840        Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
841        if (normalValueOfLogicDelete instanceof Number) {
842            return normalValueOfLogicDelete;
843        }
844        return "\"" + normalValueOfLogicDelete.toString() + "\"";
845    }
846
847
848    protected Object getLogicDeletedValue() {
849        Object deletedValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getDeletedValueOfLogicDelete();
850        if (deletedValueOfLogicDelete instanceof Number) {
851            return deletedValueOfLogicDelete;
852        }
853        return "\"" + deletedValueOfLogicDelete.toString() + "\"";
854    }
855
856}