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.row;
017
018import com.mybatisflex.core.FlexConsts;
019import com.mybatisflex.core.exception.FlexExceptions;
020import com.mybatisflex.core.paginate.Page;
021import com.mybatisflex.core.provider.RowSqlProvider;
022import com.mybatisflex.core.query.CPI;
023import com.mybatisflex.core.query.QueryColumn;
024import com.mybatisflex.core.query.QueryWrapper;
025import com.mybatisflex.core.util.CollectionUtil;
026import com.mybatisflex.core.util.MapperUtil;
027import com.mybatisflex.core.util.StringUtil;
028import org.apache.ibatis.annotations.*;
029import org.apache.ibatis.exceptions.TooManyResultsException;
030
031import java.util.Collection;
032import java.util.List;
033import java.util.Map;
034
035import static com.mybatisflex.core.query.QueryMethods.count;
036
037
038public interface RowMapper {
039
040    int DEFAULT_BATCH_SIZE = 1000;
041
042    //////insert //////
043
044    /**
045     * 插入 row 到数据表
046     *
047     * @param tableName 表名
048     * @param row       数据内容,当设置有主键时,主键会自动填充
049     * @return 执行影响的行数
050     * @see RowSqlProvider#insert(Map)
051     */
052    @InsertProvider(value = RowSqlProvider.class, method = "insert")
053    int insert(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row);
054
055
056    /**
057     * 执行 insert sql 语句
058     *
059     * @param sql  insert sql 语句
060     * @param args 参数
061     * @return 执行影响的行数
062     * @see Db#insertBySql(String, Object...)
063     * @see RowSqlProvider#providerRawSql(Map)
064     */
065    @InsertProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
066    int insertBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
067
068
069    /**
070     * 批量插入 rows 到数据表
071     * <p>
072     * 注意,批量插入中,只会根据第一条 row 数据来构建 Sql 插入字段,若每条数据字段不一致,可能造成个别字段无法插入的情况
073     *
074     * @param tableName 表名
075     * @param rows      数据内容,当设置有主键时,主键会自动填充
076     * @return 执行影响的行数
077     * @see RowSqlProvider#insertBatchWithFirstRowColumns(Map)
078     */
079    @InsertProvider(value = RowSqlProvider.class, method = "insertBatchWithFirstRowColumns")
080    int insertBatchWithFirstRowColumns(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
081
082
083    /////// delete /////
084
085    /**
086     * 执行 delete sql 语言
087     *
088     * @param sql  delete sql 语句
089     * @param args 参数
090     * @return 执行影响的行数
091     * @see RowSqlProvider#providerRawSql(Map)
092     */
093    @DeleteProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
094    int deleteBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
095
096    /**
097     * 根据 id 删除数据
098     *
099     * @param schema    模式
100     * @param tableName 表名
101     * @param row       id 和 值的数据,可以通过 {@link Row#ofKey(String, Object)} 来创建
102     * @return 执行影响的行数
103     */
104    default int deleteById(String schema, String tableName, Row row) {
105        return deleteById(schema, tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
106    }
107
108    /**
109     * 根据 id 删除数据
110     *
111     * @param schema     模式
112     * @param tableName  表名
113     * @param primaryKey 主键,多个主键用英文逗号隔开
114     * @param id         数据,多个主键时传入数组,例如 new Object[]{1,2}
115     * @return 执行影响的行数
116     * @see RowSqlProvider#deleteById(Map)
117     */
118    @DeleteProvider(value = RowSqlProvider.class, method = "deleteById")
119    int deleteById(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Object id);
120
121
122    /**
123     * 根据 多个 id 值删除多条数据
124     *
125     * @param schema     模式
126     * @param tableName  表名
127     * @param primaryKey 主键
128     * @param ids        id 的集合
129     * @return 执行影响的行数
130     * @see RowSqlProvider#deleteBatchByIds(Map)
131     */
132    @DeleteProvider(value = RowSqlProvider.class, method = "deleteBatchByIds")
133    int deleteBatchByIds(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Collection<?> ids);
134
135
136    /**
137     * 根据 queryWrapper 构建 where 条件来删除数据
138     *
139     * @param schema       模式
140     * @param tableName    表名
141     * @param queryWrapper queryWrapper
142     * @return 执行影响的行数
143     * @see RowSqlProvider#deleteByQuery(Map)
144     */
145    @DeleteProvider(value = RowSqlProvider.class, method = "deleteByQuery")
146    int deleteByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
147
148
149    ////////update ////
150
151    /**
152     * 执行 update sql 语句
153     *
154     * @param sql  sql 语句
155     * @param args 参数内容
156     * @return 执行影响的行数
157     * @see RowSqlProvider#providerRawSql(Map)
158     */
159    @UpdateProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
160    int updateBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
161
162
163    /**
164     * 根据主键来更新数据
165     *
166     * @param schema    模式
167     * @param tableName 表名
168     * @param row       数据,其必须包含主键数据列名和值
169     * @return 执行影响的行数
170     * @see RowSqlProvider#updateById(Map)
171     */
172    @UpdateProvider(value = RowSqlProvider.class, method = "updateById")
173    int updateById(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row);
174
175
176    /**
177     * 根据 queryWrapper 来构建 where 条件更新数据
178     *
179     * @param schema       模式
180     * @param tableName    表名
181     * @param data         更新数据
182     * @param queryWrapper queryWrapper
183     * @return 执行影响的行数
184     * @see RowSqlProvider#updateByQuery(Map)
185     */
186    @UpdateProvider(value = RowSqlProvider.class, method = "updateByQuery")
187    int updateByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row data, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
188
189
190    /**
191     * 根据主键来批量更新数据
192     * 注意:
193     * 1、此方法需要在 mysql 等链接配置需要开启 allowMultiQueries=true
194     * 2、更新成功返回的结果也可能为 0
195     *
196     * @param schema    模式
197     * @param tableName 表名
198     * @param rows      数据,其必须包含主键数据列名和值
199     * @return 执行影响的行数
200     * @see RowSqlProvider#updateBatchById(Map)
201     */
202    @UpdateProvider(value = RowSqlProvider.class, method = "updateBatchById")
203    int updateBatchById(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
204
205
206    /**
207     * 更新 entity,主要用于进行批量更新的场景
208     *
209     * @param entity 实体类
210     * @see RowSqlProvider#updateEntity(Map)
211     * @see Db#updateEntitiesBatch(Collection, int)
212     */
213    @UpdateProvider(value = RowSqlProvider.class, method = "updateEntity")
214    int updateEntity(@Param(FlexConsts.ENTITY) Object entity);
215
216
217    /**
218     * 执行类似 update table set field=field+1 where ... 的场景
219     *
220     * @param fieldName    字段名
221     * @param value        值( >=0 加,小于 0 减)
222     * @param queryWrapper 条件
223     * @see RowSqlProvider#updateNumberAddByQuery(Map)
224     */
225    @UpdateProvider(type = RowSqlProvider.class, method = "updateNumberAddByQuery")
226    int updateNumberAddByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.FIELD_NAME) String fieldName
227        , @Param(FlexConsts.VALUE) Number value, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
228
229
230    default int updateNumberAddByQuery(String tableName, String fieldName, Number value, QueryWrapper queryWrapper) {
231        return updateNumberAddByQuery(null, tableName, fieldName, value, queryWrapper);
232    }
233
234
235    ///////select /////
236
237    /**
238     * 通过原生 SQL 查询 1 条数据,要求数据必须返回 1 条内容,否则会报错
239     *
240     * @param sql  select sql 语句
241     * @param args 参数
242     * @return 返回一条数据
243     */
244    default Row selectOneBySql(String sql, Object... args) {
245        List<Row> rows = selectListBySql(sql, args);
246        if (rows == null || rows.isEmpty()) {
247            return null;
248        } else if (rows.size() == 1) {
249            return rows.get(0);
250        } else {
251            /** 当返回多条数据时,抛出异常, 保持和 Mybatis DefaultSqlSession 的统一逻辑,
252             * see: {@link org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(String, Object)} **/
253            throw new TooManyResultsException("Expected one result (or null) to be returned by selectOneBySql(), but found: " + rows.size());
254        }
255    }
256
257    /**
258     * 通过主键来查询数据
259     *
260     * @param schema    模式
261     * @param tableName 表名
262     * @param row       主键和ID的描述,通过 {@link Row#ofKey(String, Object)} 来进行构建
263     * @return 返回一条数据,或者 null
264     */
265    default Row selectOneById(String schema, String tableName, Row row) {
266        return selectOneById(schema, tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
267    }
268
269    /**
270     * 根据主键来查询数据
271     *
272     * @param schema     模式
273     * @param tableName  表名
274     * @param primaryKey 主键
275     * @param id         id 值
276     * @return row or null
277     * @see RowSqlProvider#selectOneById(Map)
278     */
279    @SelectProvider(value = RowSqlProvider.class, method = "selectOneById")
280    Row selectOneById(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Object id);
281
282
283    /**
284     * 根据 queryWrapper 来查询 1 条数据
285     *
286     * @param schema       模式
287     * @param tableName    表名
288     * @param queryWrapper queryWrapper
289     * @return row or null
290     */
291    default Row selectOneByQuery(String schema, String tableName, QueryWrapper queryWrapper) {
292        List<Row> rows = selectListByQuery(schema, tableName, queryWrapper.limit(1));
293        if (rows == null || rows.isEmpty()) {
294            return null;
295        } else {
296            return rows.get(0);
297        }
298    }
299
300    /**
301     * 通过自定义 sql 来查询一个 Row 列表
302     *
303     * @param sql  自定义的 sql
304     * @param args sql 参数
305     * @return row 列表
306     */
307    @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
308    List<Row> selectListBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
309
310
311    /**
312     * 根据 queryWrapper 来查询一个 row 列表
313     *
314     * @param schema       模式
315     * @param tableName    表名
316     * @param queryWrapper queryWrapper
317     * @return row 列表
318     * @see RowSqlProvider#selectListByQuery(Map)
319     */
320    @SelectProvider(value = RowSqlProvider.class, method = "selectListByQuery")
321    List<Row> selectListByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
322
323
324    /**
325     * 查询某张表的全部数据
326     *
327     * @param schema    模式
328     * @param tableName 表名
329     * @return row 列表
330     */
331    default List<Row> selectAll(String schema, String tableName) {
332        return selectListByQuery(schema, tableName, QueryWrapper.create());
333    }
334
335    /**
336     * 通过 sql 查询某一个数据,sql 执行的结果应该只有 1 行 1 列
337     * 若返回有多列,则只取第一列的值,若有多行,则会出现 TooManyResultsException 错误
338     *
339     * @param sql  sql
340     * @param args sql 参数
341     * @return object
342     */
343    @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
344    Object selectObject(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
345
346
347    /**
348     * 通过 sql 查询多行数据,sql 执行的结果应该只有 1 列
349     *
350     * @param sql  sql 语句
351     * @param args sql 参数
352     * @return object list
353     */
354    @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
355    List<Object> selectObjectList(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
356
357
358    /**
359     * 查询数据,一般用于 select count(*)... 的语言,也可用于执行的结果只有一个数值的其他 sql
360     *
361     * @param sql  sql 语句
362     * @param args sql 参数
363     * @return 返回数据
364     */
365    default long selectCount(String sql, Object... args) {
366        Object object = selectObject(sql, args);
367        if (object == null) {
368            return 0;
369        } else if (object instanceof Number) {
370            return ((Number) object).longValue();
371        } else {
372            throw FlexExceptions.wrap("selectCount error, Can not get number value for sql: %s", sql);
373        }
374    }
375
376
377    /**
378     * 根据 queryWrapper 1 条数据
379     * queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
380     *
381     * @param schema       模式
382     * @param tableName    表名
383     * @param queryWrapper queryWrapper
384     * @return 数据
385     */
386    default Object selectObjectByQuery(String schema, String tableName, QueryWrapper queryWrapper) {
387        queryWrapper.limit(1);
388        List<Object> objects = selectObjectListByQuery(schema, tableName, queryWrapper);
389        if (objects == null || objects.isEmpty()) {
390            return null;
391        }
392        return objects.get(0);
393    }
394
395
396    /**
397     * 根据 queryWrapper 来查询数据列表
398     * queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
399     *
400     * @param queryWrapper 查询包装器
401     * @return 数据列表
402     * @see RowSqlProvider#selectListByQuery(Map)
403     */
404    @SelectProvider(type = RowSqlProvider.class, method = "selectListByQuery")
405    List<Object> selectObjectListByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema
406        , @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
407
408
409    /**
410     * 查询数据量
411     *
412     * @param schema       模式
413     * @param tableName    表名
414     * @param queryWrapper 查询包装器
415     * @return 数据量
416     */
417    default long selectCountByQuery(String schema, String tableName, QueryWrapper queryWrapper) {
418        List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
419        if (CollectionUtil.isEmpty(selectColumns)) {
420            queryWrapper.select(count());
421        }
422
423        List<Object> objects = selectObjectListByQuery(schema, tableName, queryWrapper);
424        return MapperUtil.getLongNumber(objects);
425    }
426
427
428    /**
429     * 分页查询数据
430     *
431     * @param schema       模式
432     * @param tableName    表名
433     * @param page         page 封装类
434     * @param queryWrapper 条件
435     * @return
436     */
437    default Page<Row> paginate(String schema, String tableName, Page<Row> page, QueryWrapper queryWrapper) {
438        try {
439            CPI.setFromIfNecessary(queryWrapper, schema, tableName);
440
441            // 只有 totalRow 小于 0 的时候才会去查询总量
442            // 这样方便用户做总数缓存,而非每次都要去查询总量
443            // 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
444            if (page.getTotalRow() < 0) {
445                QueryWrapper countQueryWrapper;
446                if (page.needOptimizeCountQuery()) {
447                    countQueryWrapper = MapperUtil.optimizeCountQueryWrapper(queryWrapper);
448                } else {
449                    countQueryWrapper = MapperUtil.rawCountQueryWrapper(queryWrapper);
450                }
451                page.setTotalRow(selectCountByQuery(schema, tableName, countQueryWrapper));
452            }
453
454            if (page.isEmpty()) {
455                return page;
456            }
457
458            queryWrapper.limit(page.offset(), page.getPageSize());
459
460            page.setRecords(selectListByQuery(schema, tableName, queryWrapper));
461
462            return page;
463
464        } finally {
465            // 将之前设置的 limit 清除掉
466            // 保险起见把重置代码放到 finally 代码块中
467            CPI.setLimitRows(queryWrapper, null);
468            CPI.setLimitOffset(queryWrapper, null);
469        }
470
471    }
472
473}