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