携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
今天在学习尚硅谷的一个项目。其中的一个接口的实现过程让我很有感触,所以在此记录下来。这篇文章的内容如下:
- 在mybatisplus下如何封装查询条件
- 数据库表的实体类设计
- mybatis的分页查询
1.场景引入
这个接口的作用是查询订单列表。查询订单列表时可以进行分页查询和条件查询。我们数据库表如下所示:
订单表:
订单详情表:
具体的需求是在订单表里查询订单信息,而且要在订单详情表里查询对应的课程名称,最后一起返回。
2.订单表实体类的创建
各位同学看到这里,你会发现你这实体类和数据库表不对应,比如少了主键id字段,数据插入时间和数据更新时间字段。这就是我觉得巧妙的地方,我以前都没想过可以这样做,大家看下这个实体类继承了BaseEntity类。BaseEntity类里面写了通用的数据库表字段对应的属性。以后别的实体类就不需要写那么多字段了,直接继承了(如果各位同学是用代码生成器生成的代码,可以忽略这段内容)。
@Data
@ApiModel(description = "OrderInfo")
@TableName("order_info")
public class OrderInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户id")
@TableField("user_id")
private Long userId;
@ApiModelProperty(value = "昵称")
@TableField("nick_name")
private String nickName;
@TableField("phone")
private String phone;
@ApiModelProperty(value = "原始金额")
@TableField("origin_amount")
private BigDecimal originAmount;
@ApiModelProperty(value = "优惠券减免")
@TableField("coupon_reduce")
private BigDecimal couponReduce;
@ApiModelProperty(value = "最终金额")
@TableField("final_amount")
private BigDecimal finalAmount;
@ApiModelProperty(value = "订单状态")
@TableField("order_status")
private String orderStatus;
@ApiModelProperty(value = "订单交易编号(第三方支付用)")
@TableField("out_trade_no")
private String outTradeNo;
@ApiModelProperty(value = "订单描述(第三方支付用)")
@TableField("trade_body")
private String tradeBody;
@ApiModelProperty(value = "session id")
@TableField("session_id")
private String sessionId;
@ApiModelProperty(value = "地区id")
@TableField("province")
private String province;
@ApiModelProperty(value = "支付时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("pay_time")
private Date payTime;
@ApiModelProperty(value = "失效时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("expire_time")
private Date expireTime;
}
BaseEntity类
大家看到这个类里面有数据库表字段对应的通用属性外,还有一个private Map<String,Object> param = new HashMap<>()。应为后面我们可以用这个字段来封装数据到继承了BaseEntity类的实体类里面。当然了你需要在这个的属性上加入@TableField(exist = false)。
@Data
public class BaseEntity implements Serializable {
@ApiModelProperty(value = "id")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("create_time")
private Date createTime;
@ApiModelProperty(value = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("update_time")
private Date updateTime;
@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
@JsonIgnore
@TableLogic
@TableField("is_deleted")
private Integer isDeleted;
@ApiModelProperty(value = "其他参数")
@TableField(exist = false)
private Map<String,Object> param = new HashMap<>();
}
3.MybatisPlus分页插件准备
这个步骤其实简单。首先创建一个配置类,在这个配置类里面进行简单设置即可,各位同学可以从这里进行简单复制。
@Configuration
@MapperScan("com.atguigu.ggkt.order.mapper")
public class OrderConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
只要在配置类里面用注解指定mapper的位置和对应的Interceptor即可。
4.controller代码
controller代码其实很简单,就是写明要传的参数,要查询第几页的内容,每页显示几条记录,当然了还有查询条件。这我们可以将要查询的条件,封装成一个类,然后放到参数里,这里的条件类是OrderInfoQueryVo。 然后我们需要创建一个Page,因为这个对象是我们进行分页查询所需要的参数,在创建它的时候,你可以顺便将要查询的页数和每页的记录数传给它。
这里的Result是统一返回的数据格式。每个人习惯不同,你可以安装你喜欢的方式定义统一的返回数据格式。
在这里其实有个小技巧,就是如果你的controller不知道要返回什么数据的时候,可以设定他返回map集合。
@Api(tags = "订单管理")
@RestController
@RequestMapping(value="/admin/order/orderInfo")
public class OrderInfoController {
@Autowired
private OrderInfoService orderInfoService;
//订单列表
@ApiOperation(value = "获取订单分页列表")
@GetMapping("{page}/{limit}")
public Result listOrder(@PathVariable Long page,
@PathVariable Long limit,
OrderInfoQueryVo orderInfoQueryVo){
//创建page对象
Page<OrderInfo> pageParam = new Page<>(page,limit);
Map<String,Object> map = orderInfoService.selectOrderInfoPage(pageParam,orderInfoQueryVo);
return Result.ok(map);
}
}
OrderInfoQueryVo类
@Data
public class OrderInfoQueryVo {
@ApiModelProperty(value = "用户id")
private Long userId;
@ApiModelProperty(value = "nickName")
private String nickName;
@ApiModelProperty(value = "phone")
private String phone;
@ApiModelProperty(value = "订单状态")
private Integer orderStatus;
@ApiModelProperty(value = "订单交易编号(第三方支付用)")
private String outTradeNo;
@ApiModelProperty(value = "地区id")
private String province;
@ApiModelProperty(value = "创建时间")
private String createTimeBegin;
@ApiModelProperty(value = "创建时间")
private String createTimeEnd;
}
5.service接口和service接口的实现类
public interface OrderInfoService extends IService<OrderInfo> {
Map<String, Object> selectOrderInfoPage(Page<OrderInfo> pageParam, OrderInfoQueryVo orderInfoQueryVo);
}
因为我们是按条件分页查询,所以接口实现类里面会判断相应数据是否为空,最后添加到QueryWrapper来设置查询条件。
大家注意下,在BaseEntity类里的private Map<String,Object> param = new HashMap<>()在这里派上用场了。当我们查询了订单信息后,又根据id去查询订单详情表里的课程名字,查询到后,就封装到了实体类里面的map集合里面。
Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
@Autowired
private OrderDetailService orderDetailService;
//订单列表
@Override
public Map<String, Object> selectOrderInfoPage(Page<OrderInfo> pageParam, OrderInfoQueryVo orderInfoQueryVo) {
//orderInfoQueryVo获取查询条件
Long userId = orderInfoQueryVo.getUserId();
String outTradeNo = orderInfoQueryVo.getOutTradeNo();
String phone = orderInfoQueryVo.getPhone();
String createTimeEnd = orderInfoQueryVo.getCreateTimeEnd();
String createTimeBegin = orderInfoQueryVo.getCreateTimeBegin();
Integer orderStatus = orderInfoQueryVo.getOrderStatus();
//判断条件是否为空,不为空,进行条件封装
QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(userId)){
wrapper.eq("user_id",userId);
}
if(!StringUtils.isEmpty(orderStatus)) {
wrapper.eq("order_status",orderStatus);
}
if(!StringUtils.isEmpty(outTradeNo)) {
wrapper.eq("out_trade_no",outTradeNo);
}
if(!StringUtils.isEmpty(phone)) {
wrapper.eq("phone",phone);
}
if(!StringUtils.isEmpty(createTimeBegin)) {
wrapper.ge("create_time",createTimeBegin);
}
if(!StringUtils.isEmpty(createTimeEnd)) {
wrapper.le("create_time",createTimeEnd);
}
//调用实现条件分页查询
Page<OrderInfo> pages = baseMapper.selectPage(pageParam, wrapper);
long totalCount = pages.getTotal(); //总记录数
long pageCount = pages.getPages();//总页数
List<OrderInfo> records = pages.getRecords();//每页数据集合
//订单里面包含详情内容,封装详情数据,根据订单id查询详情
records.stream().forEach(item -> {
this.getOrderDetail(item);
});
//所有需要数据封装map集合,最终返回
Map<String,Object> map = new HashMap<>();
map.put("total",totalCount);
map.put("pageCount",pageCount);
map.put("records",records);
return map;
}
//查询订单详情数据
private OrderInfo getOrderDetail(OrderInfo orderInfo) {
//订单id
Long id = orderInfo.getId();
//查询订单详情
OrderDetail orderDetail = orderDetailService.getById(id);
if (orderDetail != null) {
String courseName = orderDetail.getCourseName();
orderInfo.getParam().put("courseName",courseName);
}
return orderInfo;
}
}
因为持久层框架采用的是mybatisplus,所以mapper这里就不写了。
结果如下:
6.总结
如果下次有业务是做这样的接口,那么我们可以:
- 可以将实体类通用的部分单独做成一个类,其他实体类继承它即可。在这个通用的实体类里面我们可以定义一个map集合,以后封装数据方便。
- 如果持久层框架采用的是mybatisplus,那么分页插件大家可以直接复制上面的代码。