MyBatisPlus配合SpringBoot和vue自定义sql进行多表查询,并使用Wrapper来进行自定义sql动态进行筛选查询

问题描述

我们平时经常需要多表查询,但是多表查询有时也有好多筛选条件,每次都要写sql或者修改sql非常不方便,看到MyBatisPlus集成了查询构造器和分页插件等功能,我们自定义的sql怎么配置也可以享受这种便利呢,下面我以分页多表动态查询为例,在xml中只写基本的全部查询,筛选条件交给Wrapper来实现这种封装。这样可以有很强的扩展性。


场景模拟

假设有两张表菜品表和分类表,而展示菜品时,不但需要菜品信息,还要需要分类表的分类名称

需要用菜品表记录的分类表的category_id来查询分类表的分类名称

sql代码如下:

#分类表
CREATE TABLE `category` (
  `id` BIGINT(20) NOT NULL COMMENT '主键',
  `type` INT(11) DEFAULT NULL COMMENT '类型   1 菜品分类 2 套餐分类',
  `name` VARCHAR(64) COLLATE utf8_bin NOT NULL COMMENT '分类名称',
  `sort` INT(11) NOT NULL DEFAULT '0' COMMENT '顺序',
  `create_time` DATETIME NOT NULL COMMENT '创建时间',
  `update_time` DATETIME NOT NULL COMMENT '更新时间',
  `create_user` BIGINT(20) NOT NULL COMMENT '创建人',
  `update_user` BIGINT(20) NOT NULL COMMENT '修改人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_category_name` (`name`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='菜品及套餐分类';

#菜品表
CREATE TABLE `dish` (
  `id` BIGINT(20) NOT NULL COMMENT '主键',
  `name` VARCHAR(64) COLLATE utf8_bin NOT NULL COMMENT '菜品名称',
  `category_id` BIGINT(20) NOT NULL COMMENT '菜品分类id',
  `price` DECIMAL(10,2) DEFAULT NULL COMMENT '菜品价格',
  `code` VARCHAR(64) COLLATE utf8_bin NOT NULL COMMENT '商品码',
  `image` VARCHAR(200) COLLATE utf8_bin NOT NULL COMMENT '图片',
  `description` VARCHAR(400) COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息',
  `status` INT(11) NOT NULL DEFAULT '1' COMMENT '0 停售 1 起售',
  `sort` INT(11) NOT NULL DEFAULT '0' COMMENT '顺序',
  `create_time` DATETIME NOT NULL COMMENT '创建时间',
  `update_time` DATETIME NOT NULL COMMENT '更新时间',
  `create_user` BIGINT(20) NOT NULL COMMENT '创建人',
  `update_user` BIGINT(20) NOT NULL COMMENT '修改人',
  `is_deleted` INT(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_dish_name` (`name`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='菜品管理';

对应的两个表的实体类

/**
 * 分类
 */
@Data
public class Category implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //类型 1 菜品分类 2 套餐分类
    private Integer type;


    //分类名称
    private String name;


    //顺序
    private Integer sort;


    //创建时间
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;


    //更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    //创建人
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;


    //修改人
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
}


/**
 菜品
 */
@Data
public class Dish implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //菜品名称
    private String name;


    //菜品分类id
    private Long categoryId;


    //菜品价格
    private BigDecimal price;


    //商品码
    private String code;


    //图片
    private String image;


    //描述信息
    private String description;


    //0 停售 1 起售
    private Integer status;


    //顺序
    private Integer sort;


    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    @TableField(fill = FieldFill.INSERT)
    private Long createUser;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;


    //是否删除
    private Integer isDeleted;

}

解决方案:

建立一个新的实体类对象,将要返回的属性都写上面,其实就是继承菜品类再扩展一个分类名字

专业的术语称之为dto类,一般将此类型的类专一放在dto包下

@Data
public class DishDto extends Dish {//继承了Dish说明属性都继承过来了

    //下面的属性名称因为前端的分类的name就是categoryName
    //所以我这里返回的属性名称也要是categoryName,名字要一一对应的
    private String categoryName;
}



前端的这个属性的代码
<el-table-column
    prop="categoryName"
    label="菜品分类"
></el-table-column>

然后开始分装多表查询

步骤1、在yml文件中配置mapperxml文件的扫描位置并设置类型别名

#设置自定义sql语句,mybatis一样,先设置mapper的配置文件即mybatis-plus:mapper-locations:

#这里不设置的话默认位置就是classpath*:/mapper/**/*.xml解释**表示mapper下面可以有目录也可以没有

    **    匹配0或者更多的目录

所以这里是默认配置的记住mapper.xml文件扫描位置即可。

 

然后配置类型别名扫描包!

mybatis-plus:

#配置类型别名所对应的包

#这里如果是多个包名中间加逗号

type-aliases-package: com.ma.reggie.entity,com.ma.reggie.dto

步骤2、先在mapper接口中声明方法,后面和mybatis一样了,mapper接口的代码

@Repository
public interface DishMapper extends BaseMapper<Dish> {
    Page<DishDto> selectPageCD (@Param("page") Page<DishDto> page, @Param(Constants.WRAPPER) Wrapper<DishDto> queryWrapper);
}

因为要分页,所以返回对象是Page<DishDto>第一个参数是@Param("page") Page<DishDto> page

因为我们也想使用MyBtaisPlus对应的Wrapper条件构造器,所以我们第二个参数是@Param(Constants.WRAPPER) Wrapper<DishDto> queryWrapper

注意:这个参数对应着后面的xml文件中sql语句末尾都要加 ${ew.customSqlSegment}

这个就是使用条件构造器后后面会动态生成where的语句,所以这里的多表查询一定要用sql99语法,换句话说就是我们不能使用where关键字,不然后面再使用条件构造器会出现两个where!

对应的xml文件

<mapper namespace="com.ma.reggie.mapper.DishMapper">
    <resultMap id="dishDtoMap" type="dishDto">
        <id column="did" property="id"></id>
        <result column="name" property="name"></result>
        <result column="categoryName" property="categoryName"></result>
        <result column="price" property="price"></result>
        <result column="status" property="status"></result>
        <result column="update_time" property="updateTime"></result>
        <result column="image" property="image"></result>
    </resultMap>
    <select id="selectPageCD" resultMap="dishDtoMap">
        SELECT d.id did,d.`name` ,c.`name` categoryName,d.`price`,d.`status`,d.`update_time`,d.`image` FROM `dish` d LEFT OUTER JOIN`category` c ON d.`category_id`=c.`id` ${ew.customSqlSegment}
    </select>
</mapper>

最后可以直接在Controller控制层进行自定义的分页

代码如下:

/**
     * 菜品信息分页查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> page(int page,int pageSize,String name){
        Page<DishDto> dishDtoPage = new Page<>(page,pageSize);//创建对应自定义的分页对象
        QueryWrapper<DishDto> queryWrapper = new QueryWrapper<>();//使用条件构造器来进行限制筛选条件
        queryWrapper.like(name != null,"d.name",name);
        queryWrapper.orderByDesc("d.`update_time`");//按更新时间排序
        dishMapper.selectPageCD(dishDtoPage,queryWrapper);//最后将对应的分页对象和条件构造器都交给自定义的这个分页sql的查询中,然后返回该分页对象即可。
        return R.success(dishDtoPage);
    }
}

这里我的个人建议,自定义sql封装Wrapper时,不要使用Lambda类型的条件构造器了,感觉那个过度分装了,它直接利用实体类中的对象直接找对应的sql库中的字段名,简单的还好,一样用,但这种多表查询,比如上面,多表查询中一个是分类表中的name,一个是菜品表中的name,本应该字段名是需要对应的是d.name和c.name的,但是Lambda类型的条件构造器都会最后映射成name,会导致name字段不明确,报错!所以这里直接用QueryWrapper条件构造器,需要啥sql字段就填什么sql字段,比如我这里是要填d.name的,在QueryWrapper条件构造器中直接填d.name还不用想那么多!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值