Mybatis plus 高级

本文详细介绍了MybatisPlus在项目中的应用,包括如何快速搭建环境、实现常见的CRUD操作及高级查询技巧。针对主键策略、表名映射、全局策略配置等问题提供了具体的解决方案。

最近项目重构 dao层使用的Mybatis plus,有必要总结下。

Mybatis plus 会自动维护Mybatis 以及 MyBatis-Spring 相关依赖  所以在构建项目时候 只需要引入Mybatis plus 的maven坐标就OK了

 

Mybatis plus的核心在BaseMapper<T>

包含了各种各样的 接口方法

所以我们继承后就可以拿来使用了!

Q1:

  在插入时候 bean(包含ID) 需要 使用主键策略    (数据库ID自增)

  使用注解@Table 

   在Bean上面加上注解 @TableId(value="id", type=IdType.AUTO)

   可以配置全局策略 就不用每个表都写这个注解了! 处理下划线到驼峰命名的问题

Q2:

 MP会默认使用实体类的类名到数据库中找对应的表

 使用注解@TableName(value=“tx_user“)解决

 可以配置全局策略 加个统一的前缀,就不用每个表都写这个注解了! 前缀+实体类  是table name

 

Q3:

 Mybatis plus全局策略配置

 xx.xx.xx.GlobalConfiguration

   默认是true  解析驼峰命名 到 下划线对应

   

Q4:

 @TableField(value="last_name")  对应数据库的名字

 @TableField(exist=false)   这个字段数据库没有哦  插入时候不会报错(用的比较少)

 

Q5:

 在插入一条数据时候,获取主键值

 自动的回写到实体类Bean中 不需要额外的操作

  

Q6:

  插入时候

   方法 insert()  只有非空的才会出现在sql语句日志中

           insertAllColumn() 插入所有字段  空的也是显示的  日志打印会出来这几个字段  但是是null 结果也是一样的   返回Integer

 

Q7:

   updateById()   通用更新操作  返回Integer

   updateAllColumnByID  会插入null

Q8:

   selectById()

   selectOne( entity )  通过多个列进行查询  非空属性作为条件进行查询了  要返回一条数据 要不就报错了

    List<T> selectBatchIds(List<? extends Serializable> idList)   //通过多个Id进行查询

   LIst<T> selectByMap<columnMap>  // key 是列名"school_name" 

   List<T> selectPage(RowBounds rowBounds, Warpper<T> wrapper); //分页查询  Page的对象继承了rowBounds   底层没有使用limit  使用的Mybatis的  内存的分页方式  参数 第几页  每页显示多少条数据

      Mybatis有个PageHelper    Mybatis Plus 有自己的分页插件 

  

补充 selectList

        selectOne

        selectById

        selectAll

        selectCount

 

Q9:  (remove)

  Integer deleteById(Serializable id)

  Integer deleteByMap(@Param("crm") Map<String, Object> columnMap)    key一定要写数据库的列名

  Integer deleteBatchIds(LIst<? extends Serializable> iDList)    //批量删除  底层sql 使用的in

  

以上是常用的CRUD

 

原理分析


 

 

  BaseMapper 中提供了通用的CRUD方法

 

 

补充功能强大的条件构造器 EntityWrapper


 

 

 实体包装器,主要用于处理SQL拼接,排序,实体参数查询等。

 

 

条件参数说明

查询方式说明
setSqlSelect设置 SELECT 查询字段
whereWHERE 语句,拼接 + WHERE 条件
andAND 语句,拼接 + AND 字段=值
andNewAND 语句,拼接 + AND (字段=值)
orOR 语句,拼接 + OR 字段=值
orNewOR 语句,拼接 + OR (字段=值)
eq等于=
allEq基于 map 内容等于=
ne不等于<>
gt大于>
ge大于等于>=
lt小于<
le小于等于<=
like模糊查询 LIKE
notLike模糊查询 NOT LIKE
inIN 查询
notInNOT IN 查询
isNullNULL 值查询
isNotNullIS NOT NULL
groupBy分组 GROUP BY
havingHAVING 关键词
orderBy排序 ORDER BY
orderAscASC 排序 ORDER BY
orderDescDESC 排序 ORDER BY
existsEXISTS 条件语句
notExistsNOT EXISTS 条件语句
betweenBETWEEN 条件语句
notBetweenNOT BETWEEN 条件语句
addFilter自由拼接 SQL
last拼接在最后,例如:last("LIMIT 1")

 

 

  范围:

 .between("age",2,6);

 .like("name","jack")

 .or()  //或者使用orNew()  orNew() 之前的结果封装 与后面的进行拼接

.like("email","a")  //或者邮箱有a 的

 

  修改操作:

 userEntity 属性是要修改的值

Wrapper是条件 不停的.eq (就是where了)

  .update(UserEntity, Wrapper  )

 

  删除操作

  .update()

  封装一个添加构造器就OK了 .eq() .eq()

 

高级点的查询

 排序的:

  .orderBy()  默认升序

  .orderDesc() 降序   传入的是集合!

  .last( “desc”)   手动把SQL拼接到最后(有sql注入的风险) 

  .last("limit 1,3")

 

  

 

 

    

          

  

 

转载于:https://www.cnblogs.com/toov5/p/10629961.html

<think>我们参考引用[1]和引用[2]的内容,特别是引用[2]中关于分页参数模型设计和高级组合分页参数的思路。目标:实现一个统一封装的分页查询,支持分页、排序、时间筛选。步骤:1.设计分页参数模型(PageParam)及其扩展类(支持排序、时间筛选)。2.使用MyBatisPlus的分页拦截器(PaginationInterceptor)进行物理分页。3.在Service层中根据不同的参数构建查询条件。根据引用[2]的设计,我们可以设计如下参数类:基础分页参数(PageParam):-页码(pageNo)-每页大小(pageSize)可排序的分页参数(SortablePageParam):-继承PageParam-增加排序字段列表(List<SortingField>)可时间筛选的分页参数(FilterPageParam):-继承PageParam-增加时间筛选字段列表(List<FilteringField>)同时支持排序和筛选的参数(SearchPageParam):-可以同时继承SortablePageParam和FilterPageParam,但Java不支持多继承,所以我们可以设计为:SearchPageParamextendsSortablePageParam,然后内部包含一个FilterPageParam的字段,或者直接再创建一个同时包含两者的类。但引用[2]中采用了继承关系,并且有一个组合类(SearchPageParam)同时支持排序和筛选。我们可以这样设计:具体实现:1.基础分页参数类(PageParam)2.可排序参数类(SortablePageParam)继承PageParam,增加排序字段3.时间筛选参数类(TimeFilterPageParam)继承PageParam,增加时间筛选字段4.同时支持排序和时间筛选的参数类(SortAndTimeFilterPageParam)可以继承SortablePageParam,并增加时间筛选字段(或者单独设计一个类同时包含两者)但是,为了更好的扩展性,我们可以采用组合的方式,即:-基础分页参数(PageParam)包含基本分页信息。-排序参数(SortingParam)作为一个独立的类,包含排序字段列表。-时间筛选参数(TimeFilterParam)作为一个独立的类,包含时间筛选字段列表。然后在高级分页参数中组合这些参数。但引用[2]采用了继承,我们按照引用[2]的思路来实现。然而,引用[2]中的类设计如下:PageParam(基础分页)|SortablePageParam(增加了排序字段)|FilterPageParam(增加了时间筛选字段)但是这里FilterPageParam是直接继承PageParam的?还是继承SortablePageParam?引用[2]中画的是继承关系,但文字描述是组合。实际上,引用[2]中展示的是:PageParam├──SortablePageParam-->支持排序├──FilterPageParam-->支持筛选└──SearchPageParam-->同时支持排序+筛选(这里应该是继承某个类并组合另一个?)由于Java是单继承,所以SearchPageParam可以继承其中一个,然后包含另一个。但是引用[2]没有给出SearchPageParam的具体代码。我们调整设计:使用组合代替继承来支持多种条件的组合。重新设计:1.基础分页参数(PageParam)包含pageNo和pageSize。2.排序参数(SortingField)表示单个排序字段,包含字段名和排序方向(asc/desc)。3.时间筛选参数(TimeFilterField)表示单个时间筛选字段,包含字段名和时间范围(开始时间和结束时间)。然后,我们创建一个高级分页参数类(AdvancedPageParam):-包含PageParam(或者直接包含pageNo和pageSize,但为了复用,我们可以包含一个PageParam对象)-包含一个排序字段列表(List<SortingField>)-包含一个时间筛选字段列表(List<TimeFilterField>)这样,我们就可以在一个参数类中同时支持分页、排序和时间筛选。但是,为了与引用[2]的命名保持一致,我们也可以这样:publicclassPageQueryParam{privatePageParampageParam;//分页参数privateList<SortingField>sortingFields;//排序字段privateList<TimeFilterField>timeFilterFields;//时间筛选字段}不过,引用[2]中也有一个SearchPageParam,它同时支持排序和筛选。我们可以直接使用这个类。由于引用[2]没有给出完整代码,我们按照其思路自己实现。下面我们按照组合的方式设计参数类:参数类设计:1.PageParam(基础分页参数)2.SortingField(排序字段)3.TimeFilterField(时间范围筛选字段)然后,我们创建一个高级查询参数类(AdvancedPageQueryParam),它包含:-PageParampageParam-List<SortingField>sorts-List<TimeFilterField>timeFilters但是,为了简化,我们也可以将分页参数直接放在AdvancedPageQueryParam中作为属性(而不是嵌套一个对象),这样使用起来更方便。我们选择直接包含分页参数:publicclassAdvancedPageQueryParam{//分页参数privateIntegerpageNo=1;privateIntegerpageSize=10;//排序字段列表privateList<SortingField>sorts;//时间筛选字段列表privateList<TimeFilterField>timeFilters;}这样设计简单直接。但是,为了复用,我们可以将分页参数提取出来,这样其他地方如果只需要分页,可以单独使用PageParam。因此,我们按照引用[2]的思路,设计一个包含分页参数并扩展了其他参数的高级参数类。这里我们采用另一种方式:让高级参数类继承PageParam,然后增加排序和时间筛选字段。这样高级参数类就包含了分页参数,同时扩展了其他条件。所以:publicclassAdvancedPageQueryParamextendsPageParam{privateList<SortingField>sorts;privateList<TimeFilterField>timeFilters;}这样,AdvancedPageQueryParam就拥有了pageNo和pageSize,以及排序和时间筛选字段。接下来,我们需要在Service层中根据这个参数构建QueryWrapper。步骤:1.创建Page对象(MyBatisPlus的分页对象)用于分页。2.构建QueryWrapper,根据排序字段和时间筛选字段添加条件。示例代码:首先,定义参数类:注意:这里使用Lombok简化代码。1.PageParam.java```javaimportlombok.Data;importjavax.validation.constraints.Min;importjavax.validation.constraints.NotNull;importjava.io.Serializable;@DatapublicclassPageParamimplementsSerializable{@NotNull(message="页码不能为空")@Min(value=1,message="页码最小为1")privateIntegerpageNo=1;@NotNull(message="每页条数不能为空")@Min(value=1,message="每页条数最小为1")privateIntegerpageSize=10;}```2.SortingField.java```javaimportlombok.Data;@DatapublicclassSortingField{publicstaticfinalStringORDER_ASC="asc";publicstaticfinalStringORDER_DESC="desc";privateStringfield;//排序字段privateStringorder;//排序方向:asc/desc}```3.TimeFilterField.java```javaimportlombok.Data;importjava.time.LocalDateTime;@DatapublicclassTimeFilterField{privateStringfield;//时间字段privateLocalDateTimestartTime;//开始时间privateLocalDateTimeendTime;//结束时间}```4.AdvancedPageQueryParam.java```javaimportlombok.Data;importlombok.EqualsAndHashCode;importjava.util.List;@Data@EqualsAndHashCode(callSuper=true)publicclassAdvancedPageQueryParamextendsPageParam{privateList<SortingField>sorts;//排序字段列表privateList<TimeFilterField>timeFilters;//时间筛选字段列表}```然后,在Service层使用:假设我们有一个UserService,需要根据时间范围(比如创建时间)和排序(比如按创建时间倒序)来分页查询用户。示例Service方法:```javaimportcom.baomidou.mybatisplus.core.conditions.query.QueryWrapper;importcom.baomidou.mybatisplus.core.metadata.IPage;importcom.baomidou.mybatisplus.extension.plugins.pagination.Page;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;importjava.util.List;@ServicepublicclassUserServiceImplimplementsUserService{@ResourceprivateUserMapperuserMapper;publicIPage<User>queryAdvanced(AdvancedPageQueryParamparam){//1.构建分页对象Page<User>page=newPage<>(param.getPageNo(),param.getPageSize());//2.构建查询条件QueryWrapper<User>queryWrapper=newQueryWrapper<>();//3.处理时间筛选条件if(param.getTimeFilters()!=null&&!param.getTimeFilters().isEmpty()){for(TimeFilterFieldtimeFilter:param.getTimeFilters()){//如果开始时间和结束时间都不为空,则添加between条件if(timeFilter.getStartTime()!=null&&timeFilter.getEndTime()!=null){queryWrapper.between(timeFilter.getField(),timeFilter.getStartTime(),timeFilter.getEndTime());}//如果只有开始时间,则添加>=条件elseif(timeFilter.getStartTime()!=null){queryWrapper.ge(timeFilter.getField(),timeFilter.getStartTime());}//如果只有结束时间,则添加<=条件elseif(timeFilter.getEndTime()!=null){queryWrapper.le(timeFilter.getField(),timeFilter.getEndTime());}}}//4.处理排序条件if(param.getSorts()!=null&&!param.getSorts().isEmpty()){for(SortingFieldsort:param.getSorts()){if(SortingField.ORDER_ASC.equalsIgnoreCase(sort.getOrder())){queryWrapper.orderByAsc(sort.getField());}elseif(SortingField.ORDER_DESC.equalsIgnoreCase(sort.getOrder())){queryWrapper.orderByDesc(sort.getField());}}}//5.执行分页查询returnuserMapper.selectPage(page,queryWrapper);}}```注意:这里我们假设UserMapper继承了MyBatisPlus的BaseMapper。另外,我们还需要配置MyBatisPlus的分页拦截器:```javaimportcom.baomidou.mybatisplus.annotation.DbType;importcom.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;importcom.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassMybatisPlusConfig{@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptor=newMybatisPlusInterceptor();interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.MYSQL));//根据数据库类型选择returninterceptor;}}```这样,我们就实现了统一封装的高级分页查询,支持分页、排序和时间筛选。使用示例:前端传入参数示例(JSON):```json{"pageNo":1,"pageSize":10,"sorts":[{"field":"create_time","order":"desc"}],"timeFilters":[{"field":"create_time","startTime":"2023-01-01T00:00:00","endTime":"2023-12-31T23:59:59"}]}```这个参数会被映射到AdvancedPageQueryParam对象,然后调用queryAdvanced方法即可。注意事项:-时间格式:前端传递的时间字符串需要能被转换为LocalDateTime(例如ISO格式)。-字段名:这里的字段名对应数据库的列名(注意大小写和下划线)。-安全性:在实际应用中,需要对字段名进行安全检查,防止SQL注入。MyBatisPlus的QueryWrapper在设置字段名时是使用字符串拼接的,所以需要确保字段名是安全的(比如只允许特定的字段名)。为了安全,我们可以对字段名进行校验,例如只允许某些预定义的字段进行排序或时间筛选。可以在Service层添加校验逻辑。例如,在构建查询条件之前,检查排序字段和时间筛选字段是否在允许的字段列表中。改进后的排序处理:```java//允许排序的字段列表Set<String>allowedSortFields=newHashSet<>(Arrays.asList("create_time","update_time","name"));if(param.getSorts()!=null&&!param.getSorts().isEmpty()){for(SortingFieldsort:param.getSorts()){//检查字段是否允许排序if(!allowedSortFields.contains(sort.getField())){thrownewIllegalArgumentException("不允许按字段["+sort.getField()+"]排序");}if(SortingField.ORDER_ASC.equalsIgnoreCase(sort.getOrder())){queryWrapper.orderByAsc(sort.getField());}elseif(SortingField.ORDER_DESC.equalsIgnoreCase(sort.getOrder())){queryWrapper.orderByDesc(sort.getField());}}}```同样,对时间筛选字段也可以做类似的校验。这样,我们就完成了一个安全且灵活的高级分页查询封装。总结:-设计了分页参数、排序参数、时间筛选参数以及组合参数类。-在Service层根据参数动态构建查询条件。-使用MyBatisPlus的分页拦截器实现物理分页。-增加了字段安全性校验。以上代码示例供参考,可根据实际需求调整。</think>### MyBatis Plus 高级分页统一封装实现 下面我将详细说明如何实现支持分页、排序和时间筛选的统一封装方案,并提供完整示例代码。该方案基于 MyBatis Plus 的分页拦截器机制[^1],采用面向扩展的参数设计[^2]。 #### 一、核心参数模型设计 ```java // 基础分页参数(必须) @Data public class PageParam { @NotNull @Min(1) private Integer pageNo = 1; @NotNull @Min(1) @Max(200) private Integer pageSize = 10; } // 排序字段模型(可选) @Data public class SortingField { public static final String ORDER_ASC = "asc"; public static final String ORDER_DESC = "desc"; @NotBlank private String field; // 排序字段名 @Pattern(regexp = "asc|desc", flags = Pattern.Flag.CASE_INSENSITIVE) private String order = ORDER_DESC; // 排序方向 } // 时间筛选模型(可选) @Data @NoArgsConstructor @AllArgsConstructor public class TimeFilterField { @NotBlank private String field; // 时间字段名 @Size(min = 1, max = 2) private LocalDateTime[] range; // 时间范围 [start, end] } // 高级分页参数(组合所有功能) @Data @EqualsAndHashCode(callSuper = true) public class AdvancedPageParam extends PageParam { private List<SortingField> sorts; // 排序字段列表 private List<TimeFilterField> timeFilters; // 时间筛选列表 } ``` #### 二、统一分页处理工具类 ```java public class PageQueryUtil { /** * 构建带排序和时间筛选的QueryWrapper * @param param 高级分页参数 * @param entityClass 实体类型 * @return 配置好的QueryWrapper */ public static <T> QueryWrapper<T> buildWrapper(AdvancedPageParam param, Class<T> entityClass) { QueryWrapper<T> wrapper = new QueryWrapper<>(); // 处理排序条件 if (CollectionUtils.isNotEmpty(param.getSorts())) { param.getSorts().forEach(sort -> { if (SortingField.ORDER_ASC.equalsIgnoreCase(sort.getOrder())) { wrapper.orderByAsc(getColumnName(sort.getField(), entityClass)); } else { wrapper.orderByDesc(getColumnName(sort.getField(), entityClass)); } }); } // 处理时间筛选 if (CollectionUtils.isNotEmpty(param.getTimeFilters())) { param.getTimeFilters().forEach(filter -> { String column = getColumnName(filter.getField(), entityClass); LocalDateTime[] range = filter.getRange(); if (range.length == 1) { wrapper.ge(column, range[0]); // 单点时间:大于等于 } else if (range.length == 2) { wrapper.between(column, range[0], range[1]); // 时间区间 } }); } return wrapper; } // 安全获取数据库列名(防止SQL注入) private static <T> String getColumnName(String field, Class<T> entityClass) { // 实际实现应使用反射获取实体字段对应的列名 // 此处简化演示,生产环境建议使用MyBatis Plus的TableInfoHelper return StringUtils.camelToUnderline(field); } } ``` #### 三、Service层实现示例 ```java @Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserMapper userMapper; @Override public IPage<User> queryByAdvancedParam(AdvancedPageParam param) { // 1. 构建分页对象 Page<User> page = new Page<>(param.getPageNo(), param.getPageSize()); // 2. 构建动态查询条件 QueryWrapper<User> wrapper = PageQueryUtil.buildWrapper(param, User.class); // 3. 执行分页查询 return userMapper.selectPage(page, wrapper); } } ``` #### 四、Controller层调用示例 ```java @RestController @RequestMapping("/users") public class UserController { private final UserService userService; @PostMapping("/advanced-query") public R<IPage<User>> advancedQuery(@Valid @RequestBody AdvancedPageParam param) { return R.success(userService.queryByAdvancedParam(param)); } } ``` #### 五、前端请求示例(JSON) ```json { "pageNo": 2, "pageSize": 15, "sorts": [ {"field": "createTime", "order": "desc"}, {"field": "username", "order": "asc"} ], "timeFilters": [ { "field": "createTime", "range": ["2023-01-01T00:00:00", "2023-12-31T23:59:59"] }, { "field": "updateTime", "range": ["2023-06-01T00:00:00"] // 单时间点表示 >= } ] } ``` ### 关键实现说明: 1. **分页核心机制** MyBatis Plus 通过 `PaginationInterceptor` 拦截 SQL 自动添加 `LIMIT` 分页语句[^1] 2. **安全处理**: - 字段名转换:将驼峰字段转为下划线列名 - 正则校验:排序方向只允许 `asc/desc` - 范围控制:每页条数限制 1-200 3. **动态条件构建**: ```mermaid graph TD A[接收请求参数] --> B{验证参数合法性} B -->|有效| C[构建QueryWrapper] C --> D[添加排序条件] C --> E[添加时间范围条件] D --> F[执行分页查询] E --> F F --> G[返回分页结果] ``` 4. **扩展性设计**: - 可单独使用基础分页(PageParam) - 可组合排序(SortablePageParam) - 可组合时间筛选(TimeFilterPageParam) - 全功能组合(AdvancedPageParam) > **最佳实践建议**: > 1. 在 `getColumnName()` 方法中实现字段白名单校验 > 2. 对时间范围参数进行合理性校验(如结束时间不能早于开始时间) > 3. 使用 Spring Validation 进行参数校验(如 `@Valid` 注解) > 4. 重要操作添加审计日志记录 此方案通过统一参数封装和工具类实现,显著提高了分页查询代码的复用性和可维护性,同时保持高度灵活性[^2]。 --- ### 相关问题 1. **如何防止 MyBatis Plus 分页查询中的 SQL 注入风险?** > 解决方案包括:字段名白名单校验、使用预编译语句、禁用 ${} 拼接等 2. **MyBatis Plus 分页查询性能优化的常见手段有哪些?** > 可考虑:分页语句优化、索引优化、避免 count(1) 的替代方案等 3. **如何实现跨多表的高级分页查询?** > 需要结合 Join 查询和自定义分页 SQL,可使用 MyBatis Plus 的 `selectPage(Page, QueryWrapper)` 重载方法 4. **时间范围查询如何处理时区问题?** > 建议:统一使用 UTC 时间存储,前端按用户时区转换,或使用数据库时区转换函数 5. **分页数据如何高效导出为 Excel?** > 可采用:分批查询写入、异步导出、压缩传输等技术方案 [^1]: MyBatis Plus 的分页拦截器机制会自动处理物理分页语句 [^2]: 通过参数模型继承和组合实现高扩展性的分页设计
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值