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.StringUtil;
026import org.apache.ibatis.annotations.*;
027import org.apache.ibatis.exceptions.TooManyResultsException;
028
029import java.util.Collection;
030import java.util.List;
031import java.util.Map;
032
033
034public interface RowMapper {
035
036    //////insert //////
037
038    /**
039     * 插入 row 到数据表
040     *
041     * @param tableName 表名
042     * @param row       数据内容,当设置有主键时,主键会自动填充
043     * @return 执行影响的行数
044     * @see RowSqlProvider#insert(Map)
045     */
046    @InsertProvider(value = RowSqlProvider.class, method = "insert")
047    int insert(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row);
048
049
050    /**
051     * 执行 insert sql 语句
052     *
053     * @param sql  insert sql 语句
054     * @param args 参数
055     * @return 执行影响的行数
056     * @see Db#insertBySql(String, Object...)
057     */
058    @InsertProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
059    int insertBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
060
061
062    /**
063     * 批量插入 rows 到数据表
064     * <p>
065     * 注意,批量插入中,只会根据第一条 row 数据来构建 Sql 插入字段,若每条数据字段不一致,可能造成个别字段无法插入的情况
066     *
067     * @param tableName 表名
068     * @param rows      数据内容,当设置有主键时,主键会自动填充
069     * @return 执行影响的行数
070     * @see RowSqlProvider#insertBatchWithFirstRowColumns(Map)
071     */
072    @InsertProvider(value = RowSqlProvider.class, method = "insertBatchWithFirstRowColumns")
073    int insertBatchWithFirstRowColumns(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
074
075
076    /////// delete /////
077
078    /**
079     * 执行 delete sql 语言
080     *
081     * @param sql  delete sql 语句
082     * @param args 参数
083     * @return 执行影响的行数
084     */
085    @DeleteProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
086    int deleteBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
087
088    /**
089     * 根据 id 删除数据
090     *
091     * @param tableName 表名
092     * @param row       id 和 值的数据,可以通过 {@link Row#ofKey(String, Object)} 来创建
093     * @return 执行影响的行数
094     */
095    default int deleteById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row) {
096        return deleteById(tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
097    }
098
099    /**
100     * 根据 id 删除数据
101     *
102     * @param tableName  表名
103     * @param primaryKey 主键,多个主键用英文逗号隔开
104     * @param id         数据,多个主键时传入数组,例如 new Object[]{1,2}
105     * @return 执行影响的行数
106     * @see RowSqlProvider#deleteById(Map)
107     */
108    @DeleteProvider(value = RowSqlProvider.class, method = "deleteById")
109    int deleteById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Object id);
110
111
112    /**
113     * 根据 多个 id 值删除多条数据
114     *
115     * @param tableName  表名
116     * @param primaryKey 主键
117     * @param ids        id 的集合
118     * @return 执行影响的行数
119     * @see RowSqlProvider#deleteBatchByIds(Map)
120     */
121    @DeleteProvider(value = RowSqlProvider.class, method = "deleteBatchByIds")
122    int deleteBatchByIds(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Collection<?> ids);
123
124
125    /**
126     * 根据 queryWrapper 构建 where 条件来删除数据
127     *
128     * @param tableName    表名
129     * @param queryWrapper queryWrapper
130     * @return 执行影响的行数
131     * @see RowSqlProvider#deleteByQuery(Map)
132     */
133    @DeleteProvider(value = RowSqlProvider.class, method = "deleteByQuery")
134    int deleteByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
135
136
137    ////////update ////
138
139    /**
140     * 执行 update sql 语句
141     *
142     * @param sql  sql 语句
143     * @param args 参数内容
144     * @return 执行影响的行数
145     */
146    @UpdateProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
147    int updateBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
148
149
150    /**
151     * 根据主键来更新数据
152     *
153     * @param tableName 表名
154     * @param row       数据,其必须包含主键数据列名和值
155     * @return 执行影响的行数
156     * @see RowSqlProvider#updateById(Map)
157     */
158    @UpdateProvider(value = RowSqlProvider.class, method = "updateById")
159    int updateById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row);
160
161
162    /**
163     * 根据 queryWrapper 来构建 where 条件更新数据
164     *
165     * @param tableName    表名
166     * @param data         更新数据
167     * @param queryWrapper queryWrapper
168     * @return 执行影响的行数
169     * @see RowSqlProvider#updateByQuery(Map)
170     */
171    @UpdateProvider(value = RowSqlProvider.class, method = "updateByQuery")
172    int updateByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row data, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
173
174
175    /**
176     * 根据主键来批量更新数据
177     * 注意:
178     * 1、此方法需要在 mysql 等链接配置需要开启 allowMultiQueries=true
179     * 2、更新成功返回的结果也可能为 0
180     *
181     * @param tableName 表名
182     * @param rows      数据,其必须包含主键数据列名和值
183     * @return 执行影响的行数
184     * @see RowSqlProvider#updateBatchById(Map)
185     */
186    @UpdateProvider(value = RowSqlProvider.class, method = "updateBatchById")
187    int updateBatchById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
188
189    ///////select /////
190
191    /**
192     * 通过原生 SQL 查询 1 条数据,要求数据必须返回 1 条内容,否则会报错
193     *
194     * @param sql  select sql 语句
195     * @param args 参数
196     * @return 返回一条数据
197     */
198    default Row selectOneBySql(String sql, Object... args) {
199        List<Row> rows = selectListBySql(sql, args);
200        if (rows == null || rows.isEmpty()) {
201            return null;
202        } else if (rows.size() == 1) {
203            return rows.get(0);
204        } else {
205            /** 当返回多条数据时,抛出异常, 保持和 Mybatis DefaultSqlSession 的统一逻辑,
206             * see: {@link org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(String, Object)} **/
207            throw new TooManyResultsException("Expected one result (or null) to be returned by selectOneBySql(), but found: " + rows.size());
208        }
209    }
210
211    /**
212     * 通过主键来查询数据
213     *
214     * @param tableName 表名
215     * @param row       主键和ID的描述,通过 {@link Row#ofKey(String, Object)} 来进行构建
216     * @return 返回一条数据,或者 null
217     */
218    default Row selectOneById(String tableName, Row row) {
219        return selectOneById(tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
220    }
221
222
223    /**
224     * 根据主键来查询数据
225     *
226     * @param tableName  表名
227     * @param primaryKey 主键
228     * @param id         id 值
229     * @return row or null
230     * @see RowSqlProvider#selectOneById(Map)
231     */
232    @SelectProvider(value = RowSqlProvider.class, method = "selectOneById")
233    Row selectOneById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Object id);
234
235
236    /**
237     * 根据 queryWrapper 来查询 1 条数据
238     *
239     * @param tableName    表名
240     * @param queryWrapper queryWrapper
241     * @return row or null
242     */
243    default Row selectOneByQuery(String tableName, QueryWrapper queryWrapper) {
244        List<Row> rows = selectListByQuery(tableName, queryWrapper.limit(1));
245        if (rows == null || rows.isEmpty()) {
246            return null;
247        } else {
248            return rows.get(0);
249        }
250    }
251
252    /**
253     * 通过自定义 sql 来查询一个 Row 列表
254     *
255     * @param sql  自定义的 sql
256     * @param args sql 参数
257     * @return row 列表
258     */
259    @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
260    List<Row> selectListBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
261
262
263    /**
264     * 根据 queryWrapper 来查询一个 row 列表
265     *
266     * @param tableName    表名
267     * @param queryWrapper queryWrapper
268     * @return row 列表
269     * @see RowSqlProvider#selectListByQuery(Map)
270     */
271    @SelectProvider(value = RowSqlProvider.class, method = "selectListByQuery")
272    List<Row> selectListByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
273
274
275    /**
276     * 查询某张表的全部数据
277     *
278     * @param tableName 表名
279     * @return row 列表
280     */
281    default List<Row> selectAll(@Param(FlexConsts.TABLE_NAME) String tableName) {
282        return selectListByQuery(tableName, QueryWrapper.create());
283    }
284
285    /**
286     * 通过 sql 查询某一个数据,sql 执行的结果应该只有 1 行 1 列
287     * 若返回有多列,则只取第一列的值,若有多行,则会出现 TooManyResultsException 错误
288     *
289     * @param sql  sql
290     * @param args sql 参数
291     * @return object
292     */
293    @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
294    Object selectObject(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
295
296
297    /**
298     * 通过 sql 查询多行数据,sql 执行的结果应该只有 1 列
299     *
300     * @param sql  sql 语句
301     * @param args sql 参数
302     * @return object list
303     */
304    @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
305    List<Object> selectObjectList(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
306
307
308    /**
309     * 查询数据,一般用于 select count(*)... 的语言,也可用于执行的结果只有一个数值的其他 sql
310     *
311     * @param sql  sql 语句
312     * @param args sql 参数
313     * @return 返回数据
314     */
315    default long selectCount(String sql, Object... args) {
316        Object object = selectObject(sql, args);
317        if (object == null) {
318            return 0;
319        } else if (object instanceof Number) {
320            return ((Number) object).longValue();
321        } else {
322            throw FlexExceptions.wrap("selectCount error, Can not get number value for sql: %s", sql);
323        }
324    }
325
326
327    /**
328     * 根据 queryWrapper 来查询数量
329     *
330     * @param tableName    表名
331     * @param queryWrapper queryWrapper
332     * @return 数量
333     * @see RowSqlProvider#selectCountByQuery(Map)
334     */
335    @SelectProvider(value = RowSqlProvider.class, method = "selectCountByQuery")
336    long selectCountByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
337
338
339    /**
340     * 分页查询数据
341     *
342     * @param tableName    表名
343     * @param page         page 封装类
344     * @param queryWrapper 条件
345     * @return
346     */
347    default Page<Row> paginate(String tableName, Page<Row> page, QueryWrapper queryWrapper) {
348
349        List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
350
351        // 只有 totalRow 小于 0 的时候才会去查询总量
352        // 这样方便用户做总数缓存,而非每次都要去查询总量
353        // 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
354        if (page.getTotalRow() < 0) {
355
356            //清除group by 去查询数据
357            CPI.setGroupByColumns(queryWrapper, null);
358            long count = selectCountByQuery(tableName, queryWrapper);
359            page.setTotalRow(count);
360        }
361
362        if (page.getTotalRow() == 0 || page.getPageNumber() > page.getTotalPage()) {
363            return page;
364        }
365
366        //恢复数量查询清除的 groupBy
367        CPI.setGroupByColumns(queryWrapper, groupByColumns);
368        int offset = page.getPageSize() * (page.getPageNumber() - 1);
369        queryWrapper.limit(offset, page.getPageSize());
370        List<Row> records = selectListByQuery(tableName, queryWrapper);
371        page.setRecords(records);
372        return page;
373    }
374
375
376}