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.FlexExceptions;
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
034public class EntitySqlProvider {
035
036    /**
037     * 不让实例化,使用静态方法的模式,效率更高,非静态方法每次都会实例化当前类
038     * 参考源码: {{@link org.apache.ibatis.builder.annotation.ProviderSqlSource#getBoundSql(Object)}
039     */
040    private EntitySqlProvider() {
041    }
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        if (entity == null) {
055            throw FlexExceptions.wrap("entity can not be null.");
056        }
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     * insertBatch 的 sql 构建
083     *
084     * @param params
085     * @param context
086     * @return sql
087     * @see com.mybatisflex.core.BaseMapper#insertBatch(List)
088     * @see com.mybatisflex.core.FlexConsts#METHOD_INSERT_BATCH
089     */
090    public static String insertBatch(Map params, ProviderContext context) {
091        List<Object> entities = ProviderUtil.getEntities(params);
092        if (CollectionUtil.isEmpty(entities)) {
093            throw FlexExceptions.wrap("entities can not be null or empty.");
094        }
095
096        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
097        for (Object entity : entities) {
098            tableInfo.initVersionValueIfNecessary(entity);
099            tableInfo.initTenantIdIfNecessary(entity);
100            tableInfo.initLogicDeleteValueIfNecessary(entity);
101
102            //执行 onInsert 监听器
103            tableInfo.invokeOnInsertListener(entity);
104        }
105
106
107        Object[] allValues = FlexConsts.EMPTY_ARRAY;
108        for (Object entity : entities) {
109            allValues = ArrayUtil.concat(allValues, tableInfo.buildInsertSqlArgs(entity, false));
110        }
111
112        ProviderUtil.setSqlArgs(params, allValues);
113
114        return DialectFactory.getDialect().forInsertEntityBatch(tableInfo, entities);
115    }
116
117
118    /**
119     * deleteById 的 sql 构建
120     *
121     * @param params
122     * @param context
123     * @return sql
124     * @see com.mybatisflex.core.BaseMapper#deleteById(Serializable)
125     */
126    public static String deleteById(Map params, ProviderContext context) {
127        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
128        if (ArrayUtil.isEmpty(primaryValues)) {
129            throw FlexExceptions.wrap("primaryValues can not be null or empty.");
130        }
131
132        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
133
134        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
135        ProviderUtil.setSqlArgs(params, allValues);
136
137        return DialectFactory.getDialect().forDeleteEntityById(tableInfo);
138    }
139
140
141    /**
142     * deleteBatchByIds 的 sql 构建
143     *
144     * @param params
145     * @param context
146     * @return sql
147     * @see com.mybatisflex.core.BaseMapper#deleteBatchByIds(Collection)
148     */
149    public static String deleteBatchByIds(Map params, ProviderContext context) {
150        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
151        if (ArrayUtil.isEmpty(primaryValues)) {
152            throw FlexExceptions.wrap("primaryValues can not be null or empty.");
153        }
154
155        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
156
157        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
158        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(primaryValues, tenantIdArgs));
159
160        return DialectFactory.getDialect().forDeleteEntityBatchByIds(tableInfo, primaryValues);
161    }
162
163
164    /**
165     * deleteByQuery 的 sql 构建
166     *
167     * @param params
168     * @param context
169     * @return sql
170     * @see com.mybatisflex.core.BaseMapper#deleteByQuery(QueryWrapper)
171     */
172    public static String deleteByQuery(Map params, ProviderContext context) {
173        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
174        if (queryWrapper == null) {
175            throw FlexExceptions.wrap("queryWrapper can not be null or empty.");
176        }
177
178        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
179        CPI.setFromIfNecessary(queryWrapper, tableInfo.getSchema(), tableInfo.getTableName());
180
181        tableInfo.appendConditions(null, queryWrapper);
182        ProviderUtil.setSqlArgs(params, CPI.getValueArray(queryWrapper));
183
184
185        return DialectFactory.getDialect().forDeleteEntityBatchByQuery(tableInfo, queryWrapper);
186    }
187
188
189    /**
190     * update 的 sql 构建
191     *
192     * @param params
193     * @param context
194     * @return sql
195     * @see com.mybatisflex.core.BaseMapper#update(Object, boolean)
196     */
197    public static String update(Map params, ProviderContext context) {
198        Object entity = ProviderUtil.getEntity(params);
199        if (entity == null) {
200            throw FlexExceptions.wrap("entity can not be null");
201        }
202
203        boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
204
205        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
206
207        //执行 onUpdate 监听器
208        tableInfo.invokeOnUpdateListener(entity);
209
210        Object[] updateValues = tableInfo.buildUpdateSqlArgs(entity, ignoreNulls, false);
211        Object[] primaryValues = tableInfo.buildPkSqlArgs(entity);
212        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
213
214        FlexExceptions.assertAreNotNull(primaryValues, "The value of primary key must not be null, entity[%s]", entity);
215
216        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(updateValues, primaryValues, tenantIdArgs));
217
218        return DialectFactory.getDialect().forUpdateEntity(tableInfo, entity, ignoreNulls);
219    }
220
221
222    /**
223     * updateByQuery 的 sql 构建
224     *
225     * @param params
226     * @param context
227     * @return sql
228     * @see com.mybatisflex.core.BaseMapper#updateByQuery(Object, boolean, QueryWrapper)
229     */
230    public static String updateByQuery(Map params, ProviderContext context) {
231        Object entity = ProviderUtil.getEntity(params);
232        if (entity == null) {
233            throw FlexExceptions.wrap("entity can not be null");
234        }
235        boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
236        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
237
238        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
239
240        //处理逻辑删除 和 多租户等
241        tableInfo.appendConditions(entity, queryWrapper);
242
243        Object[] values = tableInfo.buildUpdateSqlArgs(entity, ignoreNulls, true);
244        Object[] queryParams = CPI.getValueArray(queryWrapper);
245
246        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(values, queryParams));
247
248        return DialectFactory.getDialect().forUpdateEntityByQuery(tableInfo, entity, ignoreNulls, queryWrapper);
249    }
250
251    /**
252     * updateNumberByQuery 的 sql 构建
253     *
254     * @param params
255     * @param context
256     * @return sql
257     * @see com.mybatisflex.core.BaseMapper#updateNumberAddByQuery(String, Number, QueryWrapper)
258     */
259    public static String updateNumberAddByQuery(Map params, ProviderContext context) {
260        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
261
262        String fieldName = ProviderUtil.getFieldName(params);
263        Number value = (Number) ProviderUtil.getValue(params);
264
265        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
266
267        //处理逻辑删除 和 多租户等
268        tableInfo.appendConditions(null, queryWrapper);
269
270        Object[] queryParams = CPI.getValueArray(queryWrapper);
271
272        ProviderUtil.setSqlArgs(params, queryParams);
273
274        return DialectFactory.getDialect().forUpdateNumberAddByQuery(tableInfo.getSchema(), tableInfo.getTableName(), fieldName, value, queryWrapper);
275    }
276
277
278    /**
279     * selectOneById 的 sql 构建
280     *
281     * @param params
282     * @param context
283     * @return sql
284     * @see com.mybatisflex.core.BaseMapper#selectOneById(Serializable)
285     */
286    public static String selectOneById(Map params, ProviderContext context) {
287        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
288        if (ArrayUtil.isEmpty(primaryValues)) {
289            throw FlexExceptions.wrap("primaryValues can not be null or empty.");
290        }
291
292        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
293
294        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
295
296        ProviderUtil.setSqlArgs(params, allValues);
297
298        return DialectFactory.getDialect().forSelectOneEntityById(tableInfo);
299    }
300
301
302    /**
303     * selectListByIds 的 sql 构建
304     *
305     * @param params
306     * @param context
307     * @return sql
308     * @see com.mybatisflex.core.BaseMapper#selectListByIds(Collection)
309     */
310    public static String selectListByIds(Map params, ProviderContext context) {
311        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
312        if (ArrayUtil.isEmpty(primaryValues)) {
313            throw FlexExceptions.wrap("primaryValues can not be null or empty.");
314        }
315
316        TableInfo tableInfo = ProviderUtil.getTableInfo(context);
317
318        Object[] allValues = ArrayUtil.concat(primaryValues, tableInfo.buildTenantIdArgs());
319        ProviderUtil.setSqlArgs(params, allValues);
320
321        return DialectFactory.getDialect().forSelectEntityListByIds(tableInfo, primaryValues);
322    }
323
324
325    /**
326     * selectListByQuery 的 sql 构建
327     *
328     * @param params
329     * @param context
330     * @return sql
331     * @see com.mybatisflex.core.BaseMapper#selectListByQuery(QueryWrapper)
332     */
333    public static String selectListByQuery(Map params, ProviderContext context) {
334        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
335        if (queryWrapper == null) {
336            throw FlexExceptions.wrap("queryWrapper can not be null.");
337        }
338        List<TableInfo> tableInfos = getTableInfos(context, queryWrapper);
339        for (TableInfo tableInfo : tableInfos) {
340            tableInfo.appendConditions(null, queryWrapper);
341
342            CPI.setSelectColumnsIfNecessary(queryWrapper, tableInfo.getDefaultQueryColumn());
343            CPI.setFromIfNecessary(queryWrapper, tableInfo.getSchema(), tableInfo.getTableName());
344        }
345
346        Object[] values = CPI.getValueArray(queryWrapper);
347        ProviderUtil.setSqlArgs(params, values);
348
349        return DialectFactory.getDialect().forSelectByQuery(queryWrapper);
350    }
351
352    /**
353     * selectCountByQuery 的 sql 构建
354     *
355     * @param params
356     * @param context
357     * @return sql
358     * @see com.mybatisflex.core.BaseMapper#selectObjectByQuery(QueryWrapper)
359     */
360    public static String selectObjectByQuery(Map params, ProviderContext context) {
361        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
362        if (queryWrapper == null) {
363            throw FlexExceptions.wrap("queryWrapper can not be null.");
364        }
365
366        List<TableInfo> tableInfos = getTableInfos(context, queryWrapper);
367
368        for (TableInfo tableInfo : tableInfos) {
369            tableInfo.appendConditions(null, queryWrapper);
370            CPI.setFromIfNecessary(queryWrapper, tableInfo.getSchema(), tableInfo.getTableName());
371        }
372
373        Object[] values = CPI.getValueArray(queryWrapper);
374        ProviderUtil.setSqlArgs(params, values);
375
376        return DialectFactory.getDialect().forSelectByQuery(queryWrapper);
377    }
378
379
380    private static List<TableInfo> getTableInfos(ProviderContext context, QueryWrapper queryWrapper) {
381        List<TableInfo> tableInfos;
382        List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
383        if (CollectionUtil.isNotEmpty(queryTables)) {
384            tableInfos = new ArrayList<>();
385            for (QueryTable queryTable : queryTables) {
386                String tableName = queryTable.getName();
387                if (StringUtil.isNotBlank(tableName)) {
388                    TableInfo tableInfo = TableInfoFactory.ofTableName(tableName);
389                    if (tableInfo != null) {
390                        tableInfos.add(tableInfo);
391                    }
392                }
393            }
394        } else {
395            tableInfos = Collections.singletonList(ProviderUtil.getTableInfo(context));
396        }
397        return tableInfos;
398    }
399
400
401}