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.provider;
017
018import com.mybatisflex.core.FlexConsts;
019import com.mybatisflex.core.dialect.DialectFactory;
020import com.mybatisflex.core.exception.FlexAssert;
021import com.mybatisflex.core.exception.FlexExceptions;
022import com.mybatisflex.core.query.CPI;
023import com.mybatisflex.core.query.QueryTable;
024import com.mybatisflex.core.query.QueryWrapper;
025import com.mybatisflex.core.table.TableInfo;
026import com.mybatisflex.core.table.TableInfoFactory;
027import com.mybatisflex.core.util.ArrayUtil;
028import com.mybatisflex.core.util.CollectionUtil;
029import com.mybatisflex.core.util.StringUtil;
030import org.apache.ibatis.builder.annotation.ProviderContext;
031
032import java.io.Serializable;
033import java.util.*;
034
035@SuppressWarnings({"rawtypes", "DuplicatedCode"})
036public class EntitySqlProvider {
037
038    /**
039     * 不让实例化,使用静态方法的模式,效率更高,非静态方法每次都会实例化当前类
040     * 参考源码: {{@link org.apache.ibatis.builder.annotation.ProviderSqlSource#getBoundSql(Object)}
041     */
042    private EntitySqlProvider() {
043    }
044
045    /**
046     * insert 的 SQL 构建。
047     *
048     * @param params  方法参数
049     * @param context 上下文对象
050     * @return SQL 语句
051     * @see com.mybatisflex.core.BaseMapper#insert(Object)
052     */
053    public static String insert(Map params, ProviderContext context) {
054        Object entity = ProviderUtil.getEntity(params);
055
056        FlexAssert.notNull(entity, "entity can not be null.");
057
058        boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
059
060        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
061
062        //设置乐观锁版本字段的初始化数据
063        tableInfo.initVersionValueIfNecessary(entity);
064
065        //设置租户ID
066        tableInfo.initTenantIdIfNecessary(entity);
067
068        //设置逻辑删除字段的出初始化数据
069        tableInfo.initLogicDeleteValueIfNecessary(entity);
070
071        //执行 onInsert 监听器
072        tableInfo.invokeOnInsertListener(entity);
073
074        Object[] values = tableInfo.buildInsertSqlArgs(entity, ignoreNulls);
075        ProviderUtil.setSqlArgs(params, values);
076
077        return DialectFactory.getDialect().forInsertEntity(tableInfo, entity, ignoreNulls);
078    }
079
080
081    /**
082     * insertWithPk 的 SQL 构建。
083     *
084     * @param params  方法参数
085     * @param context 上下文对象
086     * @return SQL 语句
087     * @see com.mybatisflex.core.BaseMapper#insertWithPk(Object, boolean)
088     */
089    public static String insertWithPk(Map params, ProviderContext context) {
090        Object entity = ProviderUtil.getEntity(params);
091
092        FlexAssert.notNull(entity, "entity can not be null.");
093
094        boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
095
096        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
097
098        //设置乐观锁版本字段的初始化数据
099        tableInfo.initVersionValueIfNecessary(entity);
100
101        //设置租户ID
102        tableInfo.initTenantIdIfNecessary(entity);
103
104        //设置逻辑删除字段的出初始化数据
105        tableInfo.initLogicDeleteValueIfNecessary(entity);
106
107        //执行 onInsert 监听器
108        tableInfo.invokeOnInsertListener(entity);
109
110        Object[] values = tableInfo.buildInsertSqlArgsWithPk(entity, ignoreNulls);
111        ProviderUtil.setSqlArgs(params, values);
112
113        return DialectFactory.getDialect().forInsertEntityWithPk(tableInfo, entity, ignoreNulls);
114    }
115
116
117    /**
118     * insertBatch 的 SQL 构建。
119     *
120     * @param params  方法参数
121     * @param context 上下文对象
122     * @return SQL 语句
123     * @see com.mybatisflex.core.BaseMapper#insertBatch(List)
124     * @see com.mybatisflex.core.FlexConsts#METHOD_INSERT_BATCH
125     */
126    public static String insertBatch(Map params, ProviderContext context) {
127        List<Object> entities = ProviderUtil.getEntities(params);
128
129        FlexAssert.notEmpty(entities, "entities can not be null or empty.");
130
131        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
132        for (Object entity : entities) {
133            tableInfo.initVersionValueIfNecessary(entity);
134            tableInfo.initTenantIdIfNecessary(entity);
135            tableInfo.initLogicDeleteValueIfNecessary(entity);
136
137            //执行 onInsert 监听器
138            tableInfo.invokeOnInsertListener(entity);
139        }
140
141
142        Object[] allValues = FlexConsts.EMPTY_ARRAY;
143        for (Object entity : entities) {
144            allValues = ArrayUtil.concat(allValues, tableInfo.buildInsertSqlArgs(entity, false));
145        }
146
147        ProviderUtil.setSqlArgs(params, allValues);
148
149        return DialectFactory.getDialect().forInsertEntityBatch(tableInfo, entities);
150    }
151
152
153    /**
154     * deleteById 的 SQL 构建。
155     *
156     * @param params  方法参数
157     * @param context 上下文对象
158     * @return SQL 语句
159     * @see com.mybatisflex.core.BaseMapper#deleteById(Serializable)
160     */
161    public static String deleteById(Map params, ProviderContext context) {
162        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
163
164        FlexAssert.notEmpty(primaryValues, "primaryValues can not be null or empty.");
165
166        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
167
168        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
169        ProviderUtil.setSqlArgs(params, allValues);
170
171        return DialectFactory.getDialect().forDeleteEntityById(tableInfo);
172    }
173
174
175    /**
176     * deleteBatchByIds 的 SQL 构建。
177     *
178     * @param params  方法参数
179     * @param context 上下文对象
180     * @return SQL 语句
181     * @see com.mybatisflex.core.BaseMapper#deleteBatchByIds(Collection)
182     */
183    public static String deleteBatchByIds(Map params, ProviderContext context) {
184        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
185
186        FlexAssert.notEmpty(primaryValues, "primaryValues can not be null or empty.");
187
188        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
189
190        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
191        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(primaryValues, tenantIdArgs));
192
193        return DialectFactory.getDialect().forDeleteEntityBatchByIds(tableInfo, primaryValues);
194    }
195
196
197    /**
198     * deleteByQuery 的 SQL 构建。
199     *
200     * @param params  方法参数
201     * @param context 上下文对象
202     * @return SQL 语句
203     * @see com.mybatisflex.core.BaseMapper#deleteByQuery(QueryWrapper)
204     */
205    public static String deleteByQuery(Map params, ProviderContext context) {
206        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
207
208        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
209        CPI.setFromIfNecessary(queryWrapper, tableInfo.getSchema(), tableInfo.getTableName());
210
211        tableInfo.appendConditions(null, queryWrapper);
212
213        String sql = DialectFactory.getDialect().forDeleteEntityBatchByQuery(tableInfo, queryWrapper);
214        ProviderUtil.setSqlArgs(params, CPI.getValueArray(queryWrapper));
215        return sql;
216    }
217
218
219    /**
220     * update 的 SQL 构建。
221     *
222     * @param params  方法参数
223     * @param context 上下文对象
224     * @return SQL 语句
225     * @see com.mybatisflex.core.BaseMapper#update(Object, boolean)
226     */
227    public static String update(Map params, ProviderContext context) {
228        Object entity = ProviderUtil.getEntity(params);
229
230        FlexAssert.notNull(entity, "entity can not be null");
231
232        boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
233
234        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
235
236        //执行 onUpdate 监听器
237        tableInfo.invokeOnUpdateListener(entity);
238
239        Object[] updateValues = tableInfo.buildUpdateSqlArgs(entity, ignoreNulls, false);
240        Object[] primaryValues = tableInfo.buildPkSqlArgs(entity);
241        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
242
243        FlexExceptions.assertAreNotNull(primaryValues, "The value of primary key must not be null, entity[%s]", entity);
244
245        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(updateValues, primaryValues, tenantIdArgs));
246
247        return DialectFactory.getDialect().forUpdateEntity(tableInfo, entity, ignoreNulls);
248    }
249
250
251    /**
252     * updateByQuery 的 SQL 构建。
253     *
254     * @param params  方法参数
255     * @param context 上下文对象
256     * @return SQL 语句
257     * @see com.mybatisflex.core.BaseMapper#updateByQuery(Object, boolean, QueryWrapper)
258     */
259    public static String updateByQuery(Map params, ProviderContext context) {
260        Object entity = ProviderUtil.getEntity(params);
261
262        FlexAssert.notNull(entity, "entity can not be null");
263
264        boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
265        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
266
267        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
268
269        //执行 onUpdate 监听器
270        tableInfo.invokeOnUpdateListener(entity);
271
272        //处理逻辑删除 和 多租户等
273        tableInfo.appendConditions(entity, queryWrapper);
274
275        //优先构建 sql,再构建参数
276        String sql = DialectFactory.getDialect().forUpdateEntityByQuery(tableInfo, entity, ignoreNulls, queryWrapper);
277
278        Object[] values = tableInfo.buildUpdateSqlArgs(entity, ignoreNulls, true);
279        Object[] queryParams = CPI.getValueArray(queryWrapper);
280        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(values, queryParams));
281
282        return sql;
283    }
284
285    /**
286     * updateNumberByQuery 的 SQL 构建。
287     *
288     * @param params  方法参数
289     * @param context 上下文对象
290     * @return SQL 语句
291     * @see com.mybatisflex.core.BaseMapper#updateNumberAddByQuery(String, Number, QueryWrapper)
292     */
293    public static String updateNumberAddByQuery(Map params, ProviderContext context) {
294        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
295
296        String fieldName = ProviderUtil.getFieldName(params);
297        Number value = (Number) ProviderUtil.getValue(params);
298
299        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
300
301        //处理逻辑删除 和 多租户等
302        tableInfo.appendConditions(null, queryWrapper);
303
304        //优先构建 sql,再构建参数
305        String sql = DialectFactory.getDialect().forUpdateNumberAddByQuery(tableInfo.getSchema()
306            , tableInfo.getTableName(), fieldName, value, queryWrapper);
307
308        Object[] queryParams = CPI.getValueArray(queryWrapper);
309        ProviderUtil.setSqlArgs(params, queryParams);
310
311        return sql;
312    }
313
314
315    /**
316     * selectOneById 的 SQL 构建。
317     *
318     * @param params  方法参数
319     * @param context 上下文对象
320     * @return SQL 语句
321     * @see com.mybatisflex.core.BaseMapper#selectOneById(Serializable)
322     */
323    public static String selectOneById(Map params, ProviderContext context) {
324        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
325
326        FlexAssert.notEmpty(primaryValues, "primaryValues can not be null or empty.");
327
328        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
329
330        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
331
332        ProviderUtil.setSqlArgs(params, allValues);
333
334        return DialectFactory.getDialect().forSelectOneEntityById(tableInfo);
335    }
336
337
338    /**
339     * selectListByIds 的 SQL 构建。
340     *
341     * @param params  方法参数
342     * @param context 上下文对象
343     * @return SQL 语句
344     * @see com.mybatisflex.core.BaseMapper#selectListByIds(Collection)
345     */
346    public static String selectListByIds(Map params, ProviderContext context) {
347        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
348
349        FlexAssert.notEmpty(primaryValues, "primaryValues can not be null or empty.");
350
351        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
352
353        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
354        ProviderUtil.setSqlArgs(params, allValues);
355
356        return DialectFactory.getDialect().forSelectEntityListByIds(tableInfo, primaryValues);
357    }
358
359
360    /**
361     * selectListByQuery 的 SQL 构建。
362     *
363     * @param params  方法参数
364     * @param context 上下文对象
365     * @return SQL 语句
366     * @see com.mybatisflex.core.BaseMapper#selectListByQuery(QueryWrapper)
367     */
368    public static String selectListByQuery(Map params, ProviderContext context) {
369        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
370
371        appendTableConditions(context, queryWrapper, true);
372
373        //优先构建 sql,再构建参数
374        String sql = DialectFactory.getDialect().forSelectByQuery(queryWrapper);
375
376        Object[] values = CPI.getValueArray(queryWrapper);
377        ProviderUtil.setSqlArgs(params, values);
378
379        return sql;
380    }
381
382
383    /**
384     * selectCountByQuery 的 SQL 构建。
385     *
386     * @param params  方法参数
387     * @param context 上下文对象
388     * @return SQL 语句
389     * @see com.mybatisflex.core.BaseMapper#selectObjectByQuery(QueryWrapper)
390     */
391    public static String selectObjectByQuery(Map params, ProviderContext context) {
392        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
393
394        appendTableConditions(context, queryWrapper, false);
395
396        //优先构建 sql,再构建参数
397        String sql = DialectFactory.getDialect().forSelectByQuery(queryWrapper);
398
399        Object[] values = CPI.getValueArray(queryWrapper);
400        ProviderUtil.setSqlArgs(params, values);
401
402        return sql;
403    }
404
405
406    private static void appendTableConditions(ProviderContext context, QueryWrapper queryWrapper, boolean setSelectColumns) {
407        List<TableInfo> tableInfos = getTableInfos(context, queryWrapper);
408        if (CollectionUtil.isNotEmpty(tableInfos)) {
409            for (TableInfo tableInfo : tableInfos) {
410                tableInfo.appendConditions(null, queryWrapper);
411                if (setSelectColumns) {
412                    CPI.setSelectColumnsIfNecessary(queryWrapper, tableInfo.getDefaultQueryColumn());
413                }
414                CPI.setFromIfNecessary(queryWrapper, tableInfo.getSchema(), tableInfo.getTableName());
415            }
416        } else {
417            List<QueryWrapper> childQueryWrappers = CPI.getChildSelect(queryWrapper);
418            if (CollectionUtil.isNotEmpty(childQueryWrappers)) {
419                for (QueryWrapper childQueryWrapper : childQueryWrappers) {
420                    appendTableConditions(context, childQueryWrapper, setSelectColumns);
421                }
422            }
423        }
424    }
425
426
427    private static List<TableInfo> getTableInfos(ProviderContext context, QueryWrapper queryWrapper) {
428        List<TableInfo> tableInfos;
429        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
430        if (CollectionUtil.isNotEmpty(queryTables)) {
431            tableInfos = new ArrayList<>();
432            for (QueryTable queryTable : queryTables) {
433                String tableNameWithSchema = queryTable.getNameWithSchema();
434                if (StringUtil.isNotBlank(tableNameWithSchema)) {
435                    TableInfo tableInfo = TableInfoFactory.ofTableName(tableNameWithSchema);
436                    if (tableInfo != null) {
437                        tableInfos.add(tableInfo);
438                    }
439                }
440            }
441        } else {
442            tableInfos = Collections.singletonList(ProviderUtil.getTableInfo(context));
443        }
444        return tableInfos;
445    }
446
447
448}