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 ///////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 /** 331 * 通过 sql 查询多行数据,sql 执行的结果应该只有 1 列 332 * 333 * @param sql sql 语句 334 * @param args sql 参数 335 * @return object list 336 */ 337 @SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL) 338 List<Object> selectObjectList(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args); 339 340 341 /** 342 * 查询数据,一般用于 select count(*)... 的语言,也可用于执行的结果只有一个数值的其他 sql 343 * 344 * @param sql sql 语句 345 * @param args sql 参数 346 * @return 返回数据 347 */ 348 default long selectCount(String sql, Object... args) { 349 Object object = selectObject(sql, args); 350 if (object == null) { 351 return 0; 352 } else if (object instanceof Number) { 353 return ((Number) object).longValue(); 354 } else { 355 throw FlexExceptions.wrap("selectCount error, Can not get number value for sql: %s", sql); 356 } 357 } 358 359 360 /** 361 * 根据 queryWrapper 1 条数据 362 * queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where... 363 * 364 * @param schema 模式 365 * @param tableName 表名 366 * @param queryWrapper queryWrapper 367 * @return 数据 368 */ 369 default Object selectObjectByQuery(String schema, String tableName, QueryWrapper queryWrapper) { 370 queryWrapper.limit(1L); 371 List<Object> objects = selectObjectListByQuery(schema, tableName, queryWrapper); 372 if (objects == null || objects.isEmpty()) { 373 return null; 374 } 375 return objects.get(0); 376 } 377 378 379 /** 380 * 根据 queryWrapper 来查询数据列表 381 * queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where... 382 * 383 * @param queryWrapper 查询包装器 384 * @return 数据列表 385 * @see RowSqlProvider#selectListByQuery(Map) 386 */ 387 @SelectProvider(type = RowSqlProvider.class, method = "selectListByQuery") 388 List<Object> selectObjectListByQuery(@Param(FlexConsts.SCHEMA_NAME) String schema 389 , @Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper); 390 391 392 /** 393 * 查询数据量 394 * 395 * @param schema 模式 396 * @param tableName 表名 397 * @param queryWrapper 查询包装器 398 * @return 数据量 399 */ 400 default long selectCountByQuery(String schema, String tableName, QueryWrapper queryWrapper) { 401 List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper); 402 if (CollectionUtil.isEmpty(selectColumns)) { 403 queryWrapper.select(count()); 404 } 405 406 List<Object> objects = selectObjectListByQuery(schema, tableName, queryWrapper); 407 return MapperUtil.getLongNumber(objects); 408 } 409 410 411 /** 412 * 分页查询数据 413 * 414 * @param schema 模式 415 * @param tableName 表名 416 * @param page page 封装类 417 * @param queryWrapper 条件 418 * @return 419 */ 420 default Page<Row> paginate(String schema, String tableName, Page<Row> page, QueryWrapper queryWrapper) { 421 try { 422 CPI.setFromIfNecessary(queryWrapper, schema, tableName); 423 424 // 只有 totalRow 小于 0 的时候才会去查询总量 425 // 这样方便用户做总数缓存,而非每次都要去查询总量 426 // 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的 427 if (page.getTotalRow() < 0) { 428 QueryWrapper countQueryWrapper; 429 if (page.needOptimizeCountQuery()) { 430 countQueryWrapper = MapperUtil.optimizeCountQueryWrapper(queryWrapper); 431 } else { 432 countQueryWrapper = MapperUtil.rawCountQueryWrapper(queryWrapper); 433 } 434 page.setTotalRow(selectCountByQuery(schema, tableName, countQueryWrapper)); 435 } 436 437 if (!page.hasRecords()) { 438 return page; 439 } 440 441 queryWrapper.limit(page.offset(), page.getPageSize()); 442 443 page.setRecords(selectListByQuery(schema, tableName, queryWrapper)); 444 445 return page; 446 447 } finally { 448 // 将之前设置的 limit 清除掉 449 // 保险起见把重置代码放到 finally 代码块中 450 CPI.setLimitRows(queryWrapper, null); 451 CPI.setLimitOffset(queryWrapper, null); 452 } 453 454 } 455 456}