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            for (QueryColumn selectColumn : selectColumns) {
311                String selectColumnSql = CPI.toSelectSql(selectColumn, queryTables, this);
312                sqlBuilder.append(selectColumnSql);
313                if (index != selectColumns.size() - 1) {
314                    sqlBuilder.append(", ");
315                }
316                index++;
317            }
318        }
319        return sqlBuilder;
320    }
321
322
323    @Override
324    public String buildSelectCountSql(QueryWrapper queryWrapper) {
325        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
326        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
327        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
328
329        //ignore selectColumns
330        StringBuilder sqlBuilder = new StringBuilder("SELECT COUNT(*) FROM ");
331        sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
332
333
334        buildJoinSql(sqlBuilder, queryWrapper, allTables);
335        buildWhereSql(sqlBuilder, queryWrapper, allTables, true);
336        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
337        buildHavingSql(sqlBuilder, queryWrapper, allTables);
338
339        // ignore orderBy and limit
340        // buildOrderBySql(sqlBuilder, queryWrapper);
341        // buildLimitSql(sqlBuilder, queryWrapper);
342
343        return sqlBuilder.toString();
344    }
345
346    @Override
347    public String buildDeleteSql(QueryWrapper queryWrapper) {
348        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
349        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
350        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
351
352        //ignore selectColumns
353        StringBuilder sqlBuilder = new StringBuilder("DELETE FROM ");
354        sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
355
356        buildJoinSql(sqlBuilder, queryWrapper, allTables);
357        buildWhereSql(sqlBuilder, queryWrapper, allTables, false);
358        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
359        buildHavingSql(sqlBuilder, queryWrapper, allTables);
360
361        //ignore orderBy and limit
362        //buildOrderBySql(sqlBuilder, queryWrapper);
363        //buildLimitSql(sqlBuilder, queryWrapper);
364
365        return sqlBuilder.toString();
366    }
367
368    @Override
369    public String buildWhereConditionSql(QueryWrapper queryWrapper) {
370        QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
371        return whereQueryCondition != null ? whereQueryCondition.toSql(CPI.getQueryTables(queryWrapper), this) : "";
372    }
373
374    @Override
375    public String forInsertEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
376        StringBuilder sql = new StringBuilder();
377        sql.append("INSERT INTO ").append(wrap(tableInfo.getTableName()));
378
379        String[] insertColumns = tableInfo.obtainInsertColumns(entity, ignoreNulls);
380        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
381
382        StringJoiner sqlFields = new StringJoiner(", ");
383        StringJoiner sqlValues = new StringJoiner(", ");
384
385        for (String insertColumn : insertColumns) {
386            sqlFields.add(wrap(insertColumn));
387            if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
388                sqlValues.add(onInsertColumns.get(insertColumn));
389            } else {
390                sqlValues.add("?");
391            }
392        }
393
394        return sql.append("(").append(sqlFields).append(")")
395                .append(" VALUES ")
396                .append("(").append(sqlValues).append(")").toString();
397    }
398
399    @Override
400    public String forInsertEntityBatch(TableInfo tableInfo, List<Object> entities) {
401        StringBuilder sql = new StringBuilder();
402        sql.append("INSERT INTO ").append(wrap(tableInfo.getTableName()));
403        String[] insertColumns = tableInfo.obtainInsertColumns(null, false);
404        String[] warpedInsertColumns = new String[insertColumns.length];
405        for (int i = 0; i < insertColumns.length; i++) {
406            warpedInsertColumns[i] = wrap(insertColumns[i]);
407        }
408        sql.append("(").append(StringUtil.join(", ", warpedInsertColumns)).append(")");
409        sql.append(" VALUES ");
410
411        Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns();
412        for (int i = 0; i < entities.size(); i++) {
413            StringJoiner stringJoiner = new StringJoiner(", ", "(", ")");
414            for (String insertColumn : insertColumns) {
415                if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) {
416                    //直接读取 onInsert 配置的值,而不用 "?" 代替
417                    stringJoiner.add(onInsertColumns.get(insertColumn));
418                } else {
419                    stringJoiner.add("?");
420                }
421            }
422            sql.append(stringJoiner);
423            if (i != entities.size() - 1) {
424                sql.append(", ");
425            }
426        }
427
428        return sql.toString();
429    }
430
431    @Override
432    public String forDeleteEntityById(TableInfo tableInfo) {
433        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
434        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
435        //正常删除
436        if (StringUtil.isBlank(logicDeleteColumn)) {
437            String deleteByIdSql = forDeleteById(tableInfo.getTableName(), tableInfo.getPrimaryKeys());
438
439            if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
440                deleteByIdSql += " AND " + wrap(tableInfo.getTenantIdColumn()) + " IN " + buildQuestion(tenantIdArgs.length, true);
441            }
442            return deleteByIdSql;
443        }
444
445        //逻辑删除
446        StringBuilder sql = new StringBuilder();
447        String[] primaryKeys = tableInfo.getPrimaryKeys();
448
449        sql.append("UPDATE ").append(wrap(tableInfo.getTableName()));
450        sql.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
451        sql.append(" WHERE ");
452        for (int i = 0; i < primaryKeys.length; i++) {
453            if (i > 0) {
454                sql.append(" AND ");
455            }
456            sql.append(wrap(primaryKeys[i])).append(" = ?");
457        }
458
459        sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
460
461        //租户ID
462        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
463            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
464        }
465
466        return sql.toString();
467    }
468
469
470    @Override
471    public String forDeleteEntityBatchByIds(TableInfo tableInfo, Object[] primaryValues) {
472        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
473        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
474
475        //正常删除
476        if (StringUtil.isBlank(logicDeleteColumn)) {
477            String deleteSQL = forDeleteBatchByIds(tableInfo.getTableName(), tableInfo.getPrimaryKeys(), primaryValues);
478
479            //多租户
480            if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
481                deleteSQL = deleteSQL.replace(" WHERE ", " WHERE (") + ")";
482                deleteSQL += " AND " + wrap(tableInfo.getTenantIdColumn()) + " IN " + buildQuestion(tenantIdArgs.length, true);
483            }
484            return deleteSQL;
485        }
486
487        StringBuilder sql = new StringBuilder();
488        sql.append("UPDATE ");
489        sql.append(wrap(tableInfo.getTableName()));
490        sql.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
491        sql.append(" WHERE ");
492        sql.append("(");
493
494        String[] primaryKeys = tableInfo.getPrimaryKeys();
495
496        //多主键的场景
497        if (primaryKeys.length > 1) {
498            for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) {
499                if (i > 0) {
500                    sql.append(" OR ");
501                }
502                sql.append("(");
503                for (int j = 0; j < primaryKeys.length; j++) {
504                    if (j > 0) {
505                        sql.append(" AND ");
506                    }
507                    sql.append(wrap(primaryKeys[j])).append(" = ?");
508                }
509                sql.append(")");
510            }
511        }
512        // 单主键
513        else {
514            for (int i = 0; i < primaryValues.length; i++) {
515                if (i > 0) {
516                    sql.append(" OR ");
517                }
518                sql.append(wrap(primaryKeys[0])).append(" = ?");
519            }
520        }
521
522        sql.append(") AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
523
524        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
525            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
526        }
527
528        return sql.toString();
529    }
530
531    @Override
532    public String forDeleteEntityBatchByQuery(TableInfo tableInfo, QueryWrapper queryWrapper) {
533
534        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
535
536        //正常删除
537        if (StringUtil.isBlank(logicDeleteColumn)) {
538            return forDeleteByQuery(queryWrapper);
539        }
540
541
542        //逻辑删除
543        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
544        List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
545        List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
546
547        //ignore selectColumns
548        StringBuilder sqlBuilder = new StringBuilder("UPDATE ");
549        sqlBuilder.append(wrap(tableInfo.getTableName()));
550        sqlBuilder.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
551
552
553        buildJoinSql(sqlBuilder, queryWrapper, allTables);
554        buildWhereSql(sqlBuilder, queryWrapper, allTables, false);
555        buildGroupBySql(sqlBuilder, queryWrapper, allTables);
556        buildHavingSql(sqlBuilder, queryWrapper, allTables);
557
558        //ignore orderBy and limit
559        //buildOrderBySql(sqlBuilder, queryWrapper);
560        //buildLimitSql(sqlBuilder, queryWrapper);
561
562        return sqlBuilder.toString();
563    }
564
565
566    @Override
567    public String forUpdateEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
568        StringBuilder sql = new StringBuilder();
569
570        Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, false);
571        String[] primaryKeys = tableInfo.getPrimaryKeys();
572
573        sql.append("UPDATE ").append(wrap(tableInfo.getTableName())).append(" SET ");
574
575        StringJoiner stringJoiner = new StringJoiner(", ");
576
577        for (String modifyAttr : modifyAttrs) {
578            stringJoiner.add(wrap(modifyAttr) + " = ?");
579        }
580
581        Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns();
582        if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) {
583            onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + " = " + value));
584        }
585
586        //乐观锁字段
587        String versionColumn = tableInfo.getVersionColumn();
588        if (StringUtil.isNotBlank(versionColumn)) {
589            stringJoiner.add(wrap(versionColumn) + " = " + wrap(versionColumn) + " + 1 ");
590        }
591
592        sql.append(stringJoiner);
593
594        sql.append(" WHERE ");
595        for (int i = 0; i < primaryKeys.length; i++) {
596            if (i > 0) {
597                sql.append(" AND ");
598            }
599            sql.append(wrap(primaryKeys[i])).append(" = ?");
600        }
601
602        //逻辑删除条件,已删除的数据不能被修改
603        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
604        if (StringUtil.isNotBlank(logicDeleteColumn)) {
605            sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
606        }
607
608
609        //租户ID字段
610        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
611        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
612            if (tenantIdArgs.length == 1) {
613                sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" = ?");
614            } else {
615                sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
616            }
617        }
618
619        //乐观锁条件
620        if (StringUtil.isNotBlank(versionColumn)) {
621            Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn);
622            if (versionValue == null) {
623                throw FlexExceptions.wrap("The version value of entity[%s] must not be null.", entity);
624            }
625            sql.append(" AND ").append(wrap(versionColumn)).append(" = ").append(versionValue);
626        }
627
628
629        return sql.toString();
630    }
631
632    @Override
633    public String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper) {
634        StringBuilder sql = new StringBuilder();
635
636        Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true);
637
638        sql.append("UPDATE ").append(wrap(tableInfo.getTableName())).append(" SET ");
639
640        StringJoiner stringJoiner = new StringJoiner(", ");
641
642        for (String modifyAttr : modifyAttrs) {
643            stringJoiner.add(wrap(modifyAttr) + " = ?");
644        }
645
646        Map<String, String> onUpdateColumns = tableInfo.getOnUpdateColumns();
647        if (onUpdateColumns != null && !onUpdateColumns.isEmpty()) {
648            onUpdateColumns.forEach((column, value) -> stringJoiner.add(wrap(column) + " = " + value));
649        }
650
651        //乐观锁字段
652        String versionColumn = tableInfo.getVersionColumn();
653        if (StringUtil.isNotBlank(versionColumn)) {
654            stringJoiner.add(wrap(versionColumn) + " = " + wrap(versionColumn) + " + 1 ");
655        }
656
657        sql.append(stringJoiner);
658
659
660        String whereConditionSql = buildWhereConditionSql(queryWrapper);
661
662        //不允许全量更新
663        if (StringUtil.isBlank(whereConditionSql)) {
664            throw new IllegalArgumentException("Not allowed UPDATE a table without where condition.");
665        }
666
667        sql.append(" WHERE ").append(whereConditionSql);
668        return sql.toString();
669    }
670
671    @Override
672    public String forSelectOneEntityById(TableInfo tableInfo) {
673        StringBuilder sql = buildSelectColumnSql(null, tableInfo.getDefaultQueryColumn());
674        sql.append(" FROM ").append(wrap(tableInfo.getTableName()));
675        sql.append(" WHERE ");
676        String[] pKeys = tableInfo.getPrimaryKeys();
677        for (int i = 0; i < pKeys.length; i++) {
678            if (i > 0) {
679                sql.append(" AND ");
680            }
681            sql.append(wrap(pKeys[i])).append(" = ?");
682        }
683
684        //逻辑删除的情况下,需要添加逻辑删除的条件
685        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
686        if (StringUtil.isNotBlank(logicDeleteColumn)) {
687            sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
688        }
689
690        //多租户
691        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
692        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
693            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN ").append(buildQuestion(tenantIdArgs.length, true));
694        }
695
696        return sql.toString();
697    }
698
699
700    @Override
701    public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) {
702        StringBuilder sql = buildSelectColumnSql(null, tableInfo.getDefaultQueryColumn());
703        sql.append(" FROM ").append(wrap(tableInfo.getTableName()));
704        sql.append(" WHERE ");
705        String[] primaryKeys = tableInfo.getPrimaryKeys();
706
707        String logicDeleteColumn = tableInfo.getLogicDeleteColumn();
708        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
709        if (StringUtil.isNotBlank(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) {
710            sql.append("(");
711        }
712
713        //多主键的场景
714        if (primaryKeys.length > 1) {
715            for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) {
716                if (i > 0) {
717                    sql.append(" OR ");
718                }
719                sql.append("(");
720                for (int j = 0; j < primaryKeys.length; j++) {
721                    if (j > 0) {
722                        sql.append(" AND ");
723                    }
724                    sql.append(wrap(primaryKeys[j])).append(" = ?");
725                }
726                sql.append(")");
727            }
728        }
729        // 单主键
730        else {
731            for (int i = 0; i < primaryValues.length; i++) {
732                if (i > 0) {
733                    sql.append(" OR ");
734                }
735                sql.append(wrap(primaryKeys[0])).append(" = ?");
736            }
737        }
738
739        if (StringUtil.isNotBlank(logicDeleteColumn) || ArrayUtil.isNotEmpty(tenantIdArgs)) {
740            sql.append(")");
741        }
742
743
744        if (StringUtil.isNotBlank(logicDeleteColumn)) {
745            sql.append(" AND ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicNormalValue());
746        }
747
748        if (ArrayUtil.isNotEmpty(tenantIdArgs)) {
749            sql.append(" AND ").append(wrap(tableInfo.getTenantIdColumn())).append(" IN").append(buildQuestion(tenantIdArgs.length, true));
750        }
751
752        return sql.toString();
753    }
754
755
756    protected void buildJoinSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
757        List<Join> joins = CPI.getJoins(queryWrapper);
758        if (joins != null && !joins.isEmpty()) {
759            for (Join join : joins) {
760                if (!join.checkEffective()) {
761                    continue;
762                }
763                sqlBuilder.append(join.toSql(queryTables, this));
764            }
765        }
766    }
767
768
769    protected void buildWhereSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables, boolean allowNoCondition) {
770        QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
771        if (whereQueryCondition != null) {
772            String whereSql = whereQueryCondition.toSql(queryTables, this);
773            if (StringUtil.isNotBlank(whereSql)) {
774                sqlBuilder.append(" WHERE ").append(whereSql);
775            } else if (!allowNoCondition) {
776                throw new IllegalArgumentException("Not allowed DELETE a table without where condition.");
777            }
778        }
779    }
780
781
782    protected void buildGroupBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
783        List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
784        if (groupByColumns != null && !groupByColumns.isEmpty()) {
785            sqlBuilder.append(" GROUP BY ");
786            int index = 0;
787            for (QueryColumn groupByColumn : groupByColumns) {
788                String groupBy = CPI.toConditionSql(groupByColumn, queryTables, this);
789                sqlBuilder.append(groupBy);
790                if (index != groupByColumns.size() - 1) {
791                    sqlBuilder.append(", ");
792                }
793                index++;
794            }
795        }
796    }
797
798
799    protected void buildHavingSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
800        QueryCondition havingQueryCondition = CPI.getHavingQueryCondition(queryWrapper);
801        if (havingQueryCondition != null) {
802            String havingSql = havingQueryCondition.toSql(queryTables, this);
803            if (StringUtil.isNotBlank(havingSql)) {
804                sqlBuilder.append(" HAVING ").append(havingSql);
805            }
806        }
807    }
808
809
810    protected void buildOrderBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
811        List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
812        if (orderBys != null && !orderBys.isEmpty()) {
813            sqlBuilder.append(" ORDER BY ");
814            int index = 0;
815            for (QueryOrderBy orderBy : orderBys) {
816                sqlBuilder.append(orderBy.toSql(queryTables, this));
817                if (index != orderBys.size() - 1) {
818                    sqlBuilder.append(", ");
819                }
820                index++;
821            }
822        }
823    }
824
825
826    /**
827     * 构建 limit 和 offset 的参数
828     */
829    protected StringBuilder buildLimitOffsetSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, Integer limitRows, Integer limitOffset) {
830        return limitOffsetProcesser.process(sqlBuilder, queryWrapper, limitRows, limitOffset);
831    }
832
833
834    protected String buildQuestion(int count, boolean withBrackets) {
835        StringBuilder sb = new StringBuilder();
836        for (int i = 0; i < count; i++) {
837            sb.append("?");
838            if (i != count - 1) {
839                sb.append(", ");
840            }
841        }
842        return withBrackets ? "(" + sb + ")" : sb.toString();
843    }
844
845
846    protected Object getLogicNormalValue() {
847        Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
848        if (normalValueOfLogicDelete instanceof Number) {
849            return normalValueOfLogicDelete;
850        }
851        return "\"" + normalValueOfLogicDelete.toString() + "\"";
852    }
853
854
855    protected Object getLogicDeletedValue() {
856        Object deletedValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getDeletedValueOfLogicDelete();
857        if (deletedValueOfLogicDelete instanceof Number) {
858            return deletedValueOfLogicDelete;
859        }
860        return "\"" + deletedValueOfLogicDelete.toString() + "\"";
861    }
862
863}