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