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