001/*
002 *  Copyright (c) 2022-2025, 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(Collection)
123     * @see com.mybatisflex.core.FlexConsts#METHOD_INSERT_BATCH
124     */
125    public static String insertBatch(Map params, ProviderContext context) {
126        Collection<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 for execute update");
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 for execute update an entity, 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     * selectOneById 的 SQL 构建。
293     *
294     * @param params  方法参数
295     * @param context 上下文对象
296     * @return SQL 语句
297     * @see com.mybatisflex.core.BaseMapper#selectOneById(Serializable)
298     */
299    public static String selectOneById(Map params, ProviderContext context) {
300        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
301
302        FlexAssert.notEmpty(primaryValues, "primaryValues");
303
304        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
305
306        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
307
308        ProviderUtil.setSqlArgs(params, allValues);
309
310        return DialectFactory.getDialect().forSelectOneEntityById(tableInfo);
311    }
312
313
314    /**
315     * selectListByIds 的 SQL 构建。
316     *
317     * @param params  方法参数
318     * @param context 上下文对象
319     * @return SQL 语句
320     * @see com.mybatisflex.core.BaseMapper#selectListByIds(Collection)
321     */
322    public static String selectListByIds(Map params, ProviderContext context) {
323        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
324
325        FlexAssert.notEmpty(primaryValues, "primaryValues");
326
327        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
328
329        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
330        ProviderUtil.setSqlArgs(params, allValues);
331
332        return DialectFactory.getDialect().forSelectEntityListByIds(tableInfo, primaryValues);
333    }
334
335
336    /**
337     * selectListByQuery 的 SQL 构建。
338     *
339     * @param params  方法参数
340     * @param context 上下文对象
341     * @return SQL 语句
342     * @see com.mybatisflex.core.BaseMapper#selectListByQuery(QueryWrapper)
343     */
344    public static String selectListByQuery(Map params, ProviderContext context) {
345        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
346
347        appendTableConditions(context, queryWrapper, true);
348
349        //优先构建 sql,再构建参数
350        String sql = DialectFactory.getDialect().forSelectByQuery(queryWrapper);
351
352        Object[] values = CPI.getValueArray(queryWrapper);
353        ProviderUtil.setSqlArgs(params, values);
354
355        return sql;
356    }
357
358
359    /**
360     * selectCountByQuery 的 SQL 构建。
361     *
362     * @param params  方法参数
363     * @param context 上下文对象
364     * @return SQL 语句
365     * @see com.mybatisflex.core.BaseMapper#selectObjectByQuery(QueryWrapper)
366     */
367    public static String selectObjectByQuery(Map params, ProviderContext context) {
368        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
369
370        appendTableConditions(context, queryWrapper, false);
371
372        //优先构建 sql,再构建参数
373        String sql = DialectFactory.getDialect().forSelectByQuery(queryWrapper);
374
375        Object[] values = CPI.getValueArray(queryWrapper);
376        ProviderUtil.setSqlArgs(params, values);
377
378        return sql;
379    }
380
381
382    private static void appendTableConditions(ProviderContext context, QueryWrapper queryWrapper, boolean setSelectColumns) {
383        List<TableInfo> tableInfos = getTableInfos(context, queryWrapper);
384        if (CollectionUtil.isNotEmpty(tableInfos)) {
385            for (TableInfo tableInfo : tableInfos) {
386                tableInfo.appendConditions(null, queryWrapper);
387                if (setSelectColumns) {
388                    CPI.setSelectColumnsIfNecessary(queryWrapper, tableInfo.getDefaultQueryColumn());
389                }
390                CPI.setFromIfNecessary(queryWrapper, tableInfo.getSchema(), tableInfo.getTableName());
391            }
392        } else {
393            List<QueryWrapper> childQueryWrappers = CPI.getChildSelect(queryWrapper);
394            if (CollectionUtil.isNotEmpty(childQueryWrappers)) {
395                for (QueryWrapper childQueryWrapper : childQueryWrappers) {
396                    appendTableConditions(context, childQueryWrapper, setSelectColumns);
397                }
398            }
399        }
400    }
401
402
403    private static List<TableInfo> getTableInfos(ProviderContext context, QueryWrapper queryWrapper) {
404        List<TableInfo> tableInfos;
405        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
406        if (CollectionUtil.isNotEmpty(queryTables)) {
407            tableInfos = new ArrayList<>();
408            for (QueryTable queryTable : queryTables) {
409                String tableNameWithSchema = queryTable.getNameWithSchema();
410                if (StringUtil.isNotBlank(tableNameWithSchema)) {
411                    TableInfo tableInfo = TableInfoFactory.ofTableName(tableNameWithSchema);
412                    if (tableInfo != null) {
413                        tableInfos.add(tableInfo);
414                    }
415                }
416            }
417        } else {
418            tableInfos = Collections.singletonList(ProviderUtil.getTableInfo(context));
419        }
420        return tableInfos;
421    }
422
423
424}