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}