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.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 ///////select ///// 219 220 /** 221 * 通过原生 SQL 查询 1 条数据,要求数据必须返回 1 条内容,否则会报错 222 * 223 * @param sql select sql 语句 224 * @param args 参数 225 * @return 返回一条数据 226 */ 227 default Row selectOneBySql(String sql, Object... args) { 228 List<Row> rows = selectListBySql(sql, args); 229 if (rows == null || rows.isEmpty()) { 230 return null; 231 } else if (rows.size() == 1) { 232 return rows.get(0); 233 } else { 234 /** 当返回多条数据时,抛出异常, 保持和 Mybatis DefaultSqlSession 的统一逻辑, 235 * see: {@link org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(String, Object)} **/ 236 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOneBySql(), but found: " + rows.size()); 237 } 238 } 239 240 /** 241 * 通过主键来查询数据 242 * 243 * @param schema 模式 244 * @param tableName 表名 245 * @param row 主键和ID的描述,通过 {@link Row#ofKey(String, Object)} 来进行构建 246 * @return 返回一条数据,或者 null 247 */ 248 default Row selectOneById(String schema, String tableName, Row row) { 249 return selectOneById(schema, tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues()); 250 } 251 252 /** 253 * 根据主键来查询数据 254 * 255 * @param schema 模式 256 * @param tableName 表名 257 * @param primaryKey 主键 258 * @param id id 值 259 * @return row or null 260 * @see RowSqlProvider#selectOneById(Map) 261 */ 262 @SelectProvider(value = RowSqlProvider.class, method = "selectOneById") 263 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); 264 265 266 /** 267 * 根据 queryWrapper 来查询 1 条数据 268 * 269 * @param schema 模式 270 * @param tableName 表名 271 * @param queryWrapper queryWrapper 272 * @return row or null 273 */ 274 default Row selectOneByQuery(String schema, String tableName, QueryWrapper queryWrapper) { 275 List<Row> rows = selectListByQuery(schema, tableName, queryWrapper.limit(1L)); 276 if (rows == null || rows.isEmpty()) { 277 return null; 278 } else { 279 return rows.get(0); 280 } 281 } 282 283 /** 284 * 通过自定义 sql 来查询一个 Row 列表 285 * 286 * @param sql 自定义的 sql 287 * @param args sql 参数 288 * @return row 列表 289 */ 290 @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL) 291 List<Row> selectListBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args); 292 293 294 /** 295 * 根据 queryWrapper 来查询一个 row 列表 296 * 297 * @param schema 模式 298 * @param tableName 表名 299 * @param queryWrapper queryWrapper 300 * @return row 列表 301 * @see RowSqlProvider#selectListByQuery(Map) 302 */ 303 @SelectProvider(value = RowSqlProvider.class, method = "selectListByQuery") 304 List<Row> selectListByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema, @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper); 305 306 307 /** 308 * 查询某张表的全部数据 309 * 310 * @param schema 模式 311 * @param tableName 表名 312 * @return row 列表 313 */ 314 default List<Row> selectAll(String schema, String tableName) { 315 return selectListByQuery(schema, tableName, QueryWrapper.create()); 316 } 317 318 /** 319 * 通过 sql 查询某一个数据,sql 执行的结果应该只有 1 行 1 列 320 * 若返回有多列,则只取第一列的值,若有多行,则会出现 TooManyResultsException 错误 321 * 322 * @param sql sql 323 * @param args sql 参数 324 * @return object 325 */ 326 @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL) 327 Object selectObject(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args); 328 329 330 @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL) 331 Map selectFirstAndSecondColumnsAsMap(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args); 332 333 @SelectProvider(type = RowSqlProvider.class, method = "selectListByQuery") 334 Map selectFirstAndSecondColumnsAsMapByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema 335 , @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper); 336 /** 337 * 通过 sql 查询多行数据,sql 执行的结果应该只有 1 列 338 * 339 * @param sql sql 语句 340 * @param args sql 参数 341 * @return object list 342 */ 343 @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL) 344 List<Object> selectObjectList(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args); 345 346 347 /** 348 * 查询数据,一般用于 select count(*)... 的语言,也可用于执行的结果只有一个数值的其他 sql 349 * 350 * @param sql sql 语句 351 * @param args sql 参数 352 * @return 返回数据 353 */ 354 default long selectCount(String sql, Object... args) { 355 Object object = selectObject(sql, args); 356 if (object == null) { 357 return 0; 358 } else if (object instanceof Number) { 359 return ((Number) object).longValue(); 360 } else { 361 throw FlexExceptions.wrap("selectCount error, Can not get number value for sql: %s", sql); 362 } 363 } 364 365 366 /** 367 * 根据 queryWrapper 1 条数据 368 * queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where... 369 * 370 * @param schema 模式 371 * @param tableName 表名 372 * @param queryWrapper queryWrapper 373 * @return 数据 374 */ 375 default Object selectObjectByQuery(String schema, String tableName, QueryWrapper queryWrapper) { 376 queryWrapper.limit(1L); 377 List<Object> objects = selectObjectListByQuery(schema, tableName, queryWrapper); 378 if (objects == null || objects.isEmpty()) { 379 return null; 380 } 381 return objects.get(0); 382 } 383 384 385 /** 386 * 根据 queryWrapper 来查询数据列表 387 * queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where... 388 * 389 * @param queryWrapper 查询包装器 390 * @return 数据列表 391 * @see RowSqlProvider#selectListByQuery(Map) 392 */ 393 @SelectProvider(type = RowSqlProvider.class, method = "selectListByQuery") 394 List<Object> selectObjectListByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema 395 , @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper); 396 397 398 /** 399 * 查询数据量 400 * 401 * @param schema 模式 402 * @param tableName 表名 403 * @param queryWrapper 查询包装器 404 * @return 数据量 405 */ 406 default long selectCountByQuery(String schema, String tableName, QueryWrapper queryWrapper) { 407 List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper); 408 if (CollectionUtil.isEmpty(selectColumns)) { 409 queryWrapper.select(count()); 410 } 411 412 List<Object> objects = selectObjectListByQuery(schema, tableName, queryWrapper); 413 return MapperUtil.getLongNumber(objects); 414 } 415 416 417 /** 418 * 分页查询数据 419 * 420 * @param schema 模式 421 * @param tableName 表名 422 * @param page page 封装类 423 * @param queryWrapper 条件 424 * @return 425 */ 426 default Page<Row> paginate(String schema, String tableName, Page<Row> page, QueryWrapper queryWrapper) { 427 try { 428 CPI.setFromIfNecessary(queryWrapper, schema, tableName); 429 430 // 只有 totalRow 小于 0 的时候才会去查询总量 431 // 这样方便用户做总数缓存,而非每次都要去查询总量 432 // 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的 433 if (page.getTotalRow() < 0) { 434 QueryWrapper countQueryWrapper; 435 if (page.needOptimizeCountQuery()) { 436 countQueryWrapper = MapperUtil.optimizeCountQueryWrapper(queryWrapper); 437 } else { 438 countQueryWrapper = MapperUtil.rawCountQueryWrapper(queryWrapper); 439 } 440 page.setTotalRow(selectCountByQuery(schema, tableName, countQueryWrapper)); 441 } 442 443 if (!page.hasRecords()) { 444 return page; 445 } 446 447 queryWrapper.limit(page.offset(), page.getPageSize()); 448 449 page.setRecords(selectListByQuery(schema, tableName, queryWrapper)); 450 451 return page; 452 453 } finally { 454 // 将之前设置的 limit 清除掉 455 // 保险起见把重置代码放到 finally 代码块中 456 CPI.setLimitRows(queryWrapper, null); 457 CPI.setLimitOffset(queryWrapper, null); 458 } 459 460 } 461 462 463}