SpringBoot系列(二十七)MyBatisPlus联表查询

下面将讲解MyBatisPlus的联表查询,对于联表查询主要有两种方法:

  • 单表查看得到结果再拼接
  • 使用JOIN联表

https://juejin.cn/post/7055082744564416542

单表查询实现联表查询

一、一对一/一对多的单条记录查询

1.1创建Model

1.1.1BannerDO

package io.github.talelin.latticy.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@Getter
@Setter
@TableName("banner")
public class BannerDO {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String name;

    private String title;

    private String description;

    private String img;

    @JsonIgnore
    private Date createTime;

    @JsonIgnore
    private Date updateTime;

    /* @TableLogic注解参数
    value = "" 默认的原值
    delval = "" 删除后的值
    @TableLogic(value="原值",delval="改值")
    * */
    @JsonIgnore
    @TableLogic
    private Date deleteTime;
}

1.1.2、创建BannerItemDO

package io.github.talelin.latticy.model;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@TableName("banner_item")
@Getter
@Setter
public class BannerItemDO extends BaseModel {


    private String name;

    private String img;

    private String keyword;

    private Integer type;

    private Integer bannerId;

}

1.2、创建Mapper

1.2.1创建BannerMapper

package io.github.talelin.latticy.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import io.github.talelin.latticy.model.BannerDO;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface BannerMapper extends BaseMapper<BannerDO> {
    List<BannerDO> getAllBanners();

    long insertBanner(BannerDO bannerDO);

    @Select("SELECT * FROM banner")
    List<BannerDO> getAllBanners1();


}

1.2.2创建BannerItemMapper

package io.github.talelin.latticy.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import io.github.talelin.latticy.model.BannerItemDO;
import org.springframework.stereotype.Repository;

@Repository
public interface BannerItemMapper extends BaseMapper<BannerItemDO> {

}

1.3创建BO层,把Banner与BannerItme合拼

package io.github.talelin.latticy.bo;

import io.github.talelin.latticy.model.BannerDO;
import io.github.talelin.latticy.model.BannerItemDO;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;

import java.util.List;

@Data
@NoArgsConstructor
public class BannerWithItemsBO {

    private Integer id;

    private String name;

    private String title;

    private String img;

    private String description;

    List<BannerItemDO> items;

    public BannerWithItemsBO(BannerDO banner, List<BannerItemDO> items) {
        BeanUtils.copyProperties(banner, this);
        this.setItems(items);
    }
}

1.4创建Service

1.4.1创建BannerService接口

package io.github.talelin.latticy.service;

import io.github.talelin.latticy.bo.BannerWithItemsBO;
import io.github.talelin.latticy.dto.BannerDTO;
import io.github.talelin.latticy.model.BannerDO;
import io.github.talelin.latticy.vo.PageResponseVO;
import io.github.talelin.latticy.vo.UpdatedVO;

public interface BannerService {
    public PageResponseVO<BannerDO> getBanners(Integer page,Integer count);
    public void update(BannerDTO dto, Integer id);
    public void delete(Integer id);
    public BannerWithItemsBO getWithItems(Integer id);

}

1.4.2创建BannerServiceImpl实现类

package io.github.talelin.latticy.service.impl;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import io.github.talelin.autoconfigure.exception.NotFoundException;
import io.github.talelin.latticy.bo.BannerWithItemsBO;
import io.github.talelin.latticy.common.mybatis.Page;
import io.github.talelin.latticy.dto.BannerDTO;
import io.github.talelin.latticy.mapper.BannerItemMapper;
import io.github.talelin.latticy.mapper.BannerMapper;
import io.github.talelin.latticy.model.BannerDO;
import io.github.talelin.latticy.model.BannerItemDO;
import io.github.talelin.latticy.service.BannerService;
import io.github.talelin.latticy.vo.PageResponseVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BannerServiceImpl implements BannerService {

    @Autowired
    private BannerMapper bannerMapper;

    @Autowired
    private BannerItemMapper bannerItemMapper;
    
    public BannerWithItemsBO getWithItems(Integer id) {
        BannerDO banner = bannerMapper.selectById(id);
        if (banner == null) {
            throw new NotFoundException(20000);
        }
        
        /*
        * 实例化wrapper,增加条件方法一:
        * */
//        LambdaQueryWrapper<BannerItemDO> wrapper = new QueryWrapper<BannerItemDO>().lambda();
//        wrapper.eq("banner_id", id);
        /*
         * 实例化wrapper,增加条件方法二:
         * */
//        LambdaQueryWrapper<BannerItemDO> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(BannerItemDO::getBannerId, id);
//        List<BannerItemDO> bannerItems = bannerItemMapper.selectList(wrapper);
        /*
        * 条件查询方法三
        * */
        List<BannerItemDO> bannerItems =
                new LambdaQueryChainWrapper<>(bannerItemMapper)
                        .eq(BannerItemDO::getBannerId, id)
                        .list();

        return new BannerWithItemsBO(banner, bannerItems);
    }

    public void delete(Integer id) {
        BannerDO banner = bannerMapper.selectById(id);
        if (banner == null) {
            throw new NotFoundException(20000);
        }
        bannerMapper.deleteById(id);
    }

    public void update(BannerDTO dto, Integer id){
        BannerDO bannerDO = bannerMapper.selectById(id);
        if (bannerDO == null) {
            throw new NotFoundException(20000);
        }
        /*
        * org.springframework.beans.BeanUtils
        * 把dto拷贝到bannerDo中
        * */
        BeanUtils.copyProperties(dto, bannerDO);
        bannerMapper.updateById(bannerDO);

    }

    public PageResponseVO<BannerDO> getBanners(Integer page,Integer count){

        Page<BannerDO> pager = new Page<>(page,count);
        IPage<BannerDO> paging = bannerMapper.selectPage(pager, null);

        return new PageResponseVO<BannerDO>((int)paging.getTotal(), paging.getRecords(), (int)paging.getCurrent(), (int)paging.getSize());

    }

}

QueryWrapper的用法

构建SQL语句中的WHERE条件的方法

  • eq:equals,等于
  • allEq:all equals,全等于
  • ne:not equals,不等于
  • gt:greater than ,大于  >
  • ge:greater than or equals,大于等于 ≥
  • lt:less than,小于 <
  • le:less than or equals,小于等于 ≤
  • between:相当于SQL中的BETWEEN
  • notBetween
  • like:模糊匹配。like(“name”,”黄”),相当于SQL的 name like ‘%黄%’
  • likeRight:模糊匹配右半边。likeRight(“name”,”黄”),相当于SQL的name like ‘%黄%’
  • likeLeft:模糊匹配左半边。likeLeft(“name”,”黄”),相当于SQL的name like ‘%黄’
  • notLike :notLike(“name”,”黄”),相当于SQL的name not like ‘%黄%’
  • isNull
  • isNotNull
  • in
  • and :SQL连接符AND
  • or :SQL连接符OR
  • apply :用于拼接SQL,该方法可用于数据库函数,并可以动态传参
// 案例先展示需要完成的SQL语句,后展示Wrapper的写法

// 1. 名字中包含佳,且年龄小于25
// SELECT * FROM user WHERE name like '%佳%' AND age < 25
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", "佳").lt("age", 25);
List<User> users = userMapper.selectList(wrapper);
// 下面展示SQL时,仅展示WHERE条件;展示代码时, 仅展示Wrapper构建部分

// 2. 姓名为黄姓,且年龄大于等于20,小于等于40,且email字段不为空
// name like '黄%' AND age BETWEEN 20 AND 40 AND email is not null
wrapper.likeRight("name","黄").between("age", 20, 40).isNotNull("email");

// 3. 姓名为黄姓,或者年龄大于等于40,按照年龄降序排列,年龄相同则按照id升序排列
// name like '黄%' OR age >= 40 order by age desc, id asc
wrapper.likeRight("name","黄").or().ge("age",40).orderByDesc("age").orderByAsc("id");

// 4.创建日期为2021年3月22日,并且直属上级的名字为李姓
// date_format(create_time,'%Y-%m-%d') = '2021-03-22' AND manager_id IN (SELECT id FROM user WHERE name like '李%')
wrapper.apply("date_format(create_time, '%Y-%m-%d') = {0}", "2021-03-22")  // 建议采用{index}这种方式动态传参, 可防止SQL注入
				.inSql("manager_id", "SELECT id FROM user WHERE name like '李%'");
// 上面的apply, 也可以直接使用下面这种方式做字符串拼接,但当这个日期是一个外部参数时,这种方式有SQL注入的风险
wrapper.apply("date_format(create_time, '%Y-%m-%d') = '2021-03-22'");

// 5. 名字为王姓,并且(年龄小于40,或者邮箱不为空)
// name like '王%' AND (age < 40 OR email is not null)
wrapper.likeRight("name", "王").and(q -> q.lt("age", 40).or().isNotNull("email"));

// 6. 名字为王姓,或者(年龄小于40并且年龄大于20并且邮箱不为空)
// name like '王%' OR (age < 40 AND age > 20 AND email is not null)
wrapper.likeRight("name", "王").or(
				q -> q.lt("age",40)
						.gt("age",20)
						.isNotNull("email")
		);

// 7. (年龄小于40或者邮箱不为空) 并且名字为王姓
// (age < 40 OR email is not null) AND name like '王%'
wrapper.nested(q -> q.lt("age", 40).or().isNotNull("email"))
				.likeRight("name", "王");

// 8. 年龄为30,31,34,35
// age IN (30,31,34,35)
wrapper.in("age", Arrays.asList(30,31,34,35));
// 或
wrapper.inSql("age","30,31,34,35");

// 9. 年龄为30,31,34,35, 返回满足条件的第一条记录
// age IN (30,31,34,35) LIMIT 1
wrapper.in("age", Arrays.asList(30,31,34,35)).last("LIMIT 1");

// 10. 只选出id, name 列 (QueryWrapper 特有)
// SELECT id, name FROM user;
wrapper.select("id", "name");

// 11. 选出id, name, age, email, 等同于排除 manager_id 和 create_time
// 当列特别多, 而只需要排除个别列时, 采用上面的方式可能需要写很多个列, 可以采用重载的select方法,指定需要排除的列
wrapper.select(User.class, info -> {
			String columnName = info.getColumn();
			return !"create_time".equals(columnName) && !"manager_id".equals(columnName);
		});

1.5创建Controller

package io.github.talelin.latticy.controller.v1;

import io.github.talelin.core.annotation.GroupRequired;
import io.github.talelin.core.annotation.Logger;
import io.github.talelin.core.annotation.LoginRequired;
import io.github.talelin.core.annotation.PermissionMeta;
import io.github.talelin.latticy.bo.BannerWithItemsBO;
import io.github.talelin.latticy.dto.BannerDTO;
import io.github.talelin.latticy.model.BannerDO;
import io.github.talelin.latticy.service.BannerService;
import io.github.talelin.latticy.vo.DeletedVO;
import io.github.talelin.latticy.vo.PageResponseVO;
import io.github.talelin.latticy.vo.UpdatedVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Positive;

@RequestMapping("/v1/banner")
@RestController
@Validated
public class BannerController {

    @Autowired
    private BannerService bannerService;

    @GetMapping("/getBannerWithItem/{id}")
    public BannerWithItemsBO getWithItems(@PathVariable @Positive Integer id) {
        return bannerService.getWithItems(id);
    }

    @DeleteMapping("/deleteBanner/{id}")
    public DeletedVO delete(@PathVariable @Positive Integer id) {
        bannerService.delete(id);
        return new DeletedVO();
    }

    @PutMapping("/updateBanner/{id}")
    public UpdatedVO update(@RequestBody @Validated BannerDTO dto,
                            @PathVariable @Positive Integer id) {
        bannerService.update(dto, id);
        return new UpdatedVO();
    }


    @GetMapping("/page")
    public PageResponseVO<BannerDO> getBanners(
            @RequestParam(required = false, defaultValue = "0")
            @Min(value = 0) Integer page,
            @RequestParam(required = false, defaultValue = "10")
            @Min(value = 1) @Max(value = 30) Integer count
    ){
        return bannerService.getBanners(page,count);
    }


}
JAVA、基础技术、技术与框架SpringBoot系列(二十七)MyBatisPlus联表查询插图

二、一对一/一对多的多条记录查询

2.1.1BannerDO(与上例子一致)

2.1.2创建BannerItemDO (与上例子一致)

2.2.1创建BannerMapper (与上例子一致)

2.2.2创建BannerItemMapper (与上例子一致)

2.3创建BO层,把Banner与BannerItme合拼 (与上例子一致)

2.4创建Service

2.4.1创建Service接口

public List<BannerWithItemsBO> getBannerWithItemsList();

2.4.2创建Service实现类

    public List<BannerWithItemsBO> getBannerWithItemsList(){
        List<BannerDO> bannerDOList = bannerMapper.selectList(Wrappers.emptyWrapper());

        Set<Integer> bannerIds = bannerDOList.stream().map(BannerDO::getId).collect(toSet());

        List<BannerItemDO> bannerItemDOList = bannerItemMapper.selectList(Wrappers.lambdaQuery(BannerItemDO.class).in(BannerItemDO::getBannerId, bannerIds));
        /*根据bannerid分组*/
        Map<Integer, List<BannerItemDO>> hashMap = bannerItemDOList.stream().collect(groupingBy(BannerItemDO::getBannerId));

        List<BannerWithItemsBO> bannerWithItemsBOList = bannerDOList.stream().map(e ->
                new BannerWithItemsBO(e,hashMap.get(e.getId()))
        ).collect(toList());
        return bannerWithItemsBOList;
    }

2.4创建Controller

@GetMapping("/getBannerWithItemsList")
    public List<BannerWithItemsBO> getBannerWithItemsList() {
        List<BannerWithItemsBO> bannerWithItemsBOList = bannerService.getBannerWithItemsList();
        return bannerWithItemsBOList;
    }
JAVA、基础技术、技术与框架SpringBoot系列(二十七)MyBatisPlus联表查询插图1

三、使用JOIN联表

(19条消息) SpringBoot整合Mybatis-Plus—-多表关联查询(使用注解)_懒虫虫~的博客-CSDN博客_springbootmybatisplus多表查询

https://blog.csdn.net/m0_37779570/article/details/81514757

3.1创建BannerDO

package io.github.talelin.latticy.model;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;
import java.util.List;

@Getter
@Setter
@TableName("banner")
public class BannerDO {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String name;

    private String title;

    private String description;

    private String img;

    @Version
    private Integer version;

    @JsonIgnore
    private Date createTime;

    @JsonIgnore
    private Date updateTime;

    /* @TableLogic注解参数
    value = "" 默认的原值
    delval = "" 删除后的值
    @TableLogic(value="原值",delval="改值")
    * */
    @JsonIgnore
    @TableLogic
    private Date deleteTime;
    //假设我们需要查询一个banner及其下面的item,首先对 Banner 实体类稍作修改,增加 bannerItemList 集合属性
    @TableField(exist = false)
    private List<BannerItemDO> items;
}

3.2创建BannerItemDO

package io.github.talelin.latticy.model;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@TableName("banner_item")
@Getter
@Setter
public class BannerItemDO extends BaseModel {


    private String name;

    private String img;

    private String keyword;

    private Integer type;

    private Integer bannerId;

}

3.3创建BannerMapper

@Repository
public interface BannerMapper extends BaseMapper<BannerDO> {

    @Results({
            @Result(column = "id", property = "id"),
            @Result(column = "id",property = "items",
                    javaType=List.class,
                    many =@Many(select="io.github.talelin.latticy.mapper.BannerItemMapper.selectBannerItemByBannerId"))
    })
    @Select("SELECT * FROM banner where 1=1  ${ew.customSqlSegment}")
    List<BannerDO> selecBannertList(@Param(Constants.WRAPPER) Wrapper wrapper);
}

3.4创建BannerItemMapper

package io.github.talelin.latticy.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import io.github.talelin.latticy.model.BannerItemDO;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.util.List;

@Repository
public interface BannerItemMapper extends BaseMapper<BannerItemDO> {



    @Select({"select b.* from banner_item b",
            "where b.banner_id=#{banner_id}"
    })
    List<BannerItemDO> selectBannerItemByBannerId(@Param("banner_id") Integer banner_id);

}

3.5创建Service

    public List<BannerDO> getBannerWithItemsJoinList(){

        LambdaQueryWrapper<BannerDO> wrapper = new LambdaQueryWrapper<>();
        List<BannerDO> list = bannerMapper.selecBannertList(wrapper);
        return list;
    }

3.6创建Controller

    @GetMapping("/getBannerWithItemsJoinList")
    public List<BannerDO> getBannerWithItemsJoinList() {
        List<BannerDO> bannerWithItemsBOList = bannerService.getBannerWithItemsJoinList();
        return bannerWithItemsBOList;
    }
JAVA、基础技术、技术与框架SpringBoot系列(二十七)MyBatisPlus联表查询插图2

四MyBatis级联删除

通过事务保证一致性。

deleteBatchIds()

五MyBatis数据创建保存

本案例以保存Banner为例子

5.1创建BannerBO

5.2创建BannerMapper

5.3创建BannerDTO

package io.github.talelin.latticy.dto;

import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotBlank;

@Getter
@Setter
public class BannerDTO {

    @NotBlank
    @Length(min = 2, max = 20)
    private String name;

    @Length(min = 2, max = 30)
    private String title;

    @Length(min = 2, max = 256)
    private String img;

    @Length(min = 2, max = 256)
    private String description;
    
}

5.4创建Service

5.4.1创建BannerService接口

package io.github.talelin.latticy.service;

import io.github.talelin.latticy.bo.BannerWithItemsBO;
import io.github.talelin.latticy.dto.BannerDTO;
import io.github.talelin.latticy.model.BannerDO;
import io.github.talelin.latticy.vo.PageResponseVO;
import io.github.talelin.latticy.vo.UpdatedVO;

public interface BannerService {
    public PageResponseVO<BannerDO> getBanners(Integer page,Integer count);
    public void update(BannerDTO dto, Integer id);
    public void delete(Integer id);
    public BannerWithItemsBO getWithItems(Integer id);
    public void create(BannerDTO bannerDTO);
}

5.4.2创建BannerServiceImpl

package io.github.talelin.latticy.service.impl;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import io.github.talelin.autoconfigure.exception.NotFoundException;
import io.github.talelin.latticy.bo.BannerWithItemsBO;
import io.github.talelin.latticy.common.mybatis.Page;
import io.github.talelin.latticy.dto.BannerDTO;
import io.github.talelin.latticy.mapper.BannerItemMapper;
import io.github.talelin.latticy.mapper.BannerMapper;
import io.github.talelin.latticy.model.BannerDO;
import io.github.talelin.latticy.model.BannerItemDO;
import io.github.talelin.latticy.service.BannerService;
import io.github.talelin.latticy.vo.PageResponseVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BannerServiceImpl implements BannerService {

    @Autowired
    private BannerMapper bannerMapper;

    @Autowired
    private BannerItemMapper bannerItemMapper;

    public void create(BannerDTO bannerDTO) {
        BannerDO bannerDO = new BannerDO();
        BeanUtils.copyProperties(bannerDTO, bannerDO);
        bannerMapper.insert(bannerDO);
    }

    public BannerWithItemsBO getWithItems(Integer id) {
        BannerDO banner = bannerMapper.selectById(id);
        if (banner == null) {
            throw new NotFoundException(20000);
        }

        /*
        * 实例化wrapper,增加条件方法一:
        * */
//        LambdaQueryWrapper<BannerItemDO> wrapper = new QueryWrapper<BannerItemDO>().lambda();
//        wrapper.eq("banner_id", id);
        /*
         * 实例化wrapper,增加条件方法二:
         * */
//        LambdaQueryWrapper<BannerItemDO> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(BannerItemDO::getBannerId, id);
//        List<BannerItemDO> bannerItems = bannerItemMapper.selectList(wrapper);
        /*
        * 条件查询方法三
        * */
        List<BannerItemDO> bannerItems =
                new LambdaQueryChainWrapper<>(bannerItemMapper)
                        .eq(BannerItemDO::getBannerId, id)
                        .list();

        return new BannerWithItemsBO(banner, bannerItems);
    }

    public void delete(Integer id) {
        BannerDO banner = bannerMapper.selectById(id);
        if (banner == null) {
            throw new NotFoundException(20000);
        }
        bannerMapper.deleteById(id);
    }

    public void update(BannerDTO dto, Integer id){
        BannerDO bannerDO = bannerMapper.selectById(id);
        if (bannerDO == null) {
            throw new NotFoundException(20000);
        }
        /*
        * org.springframework.beans.BeanUtils
        * 把dto拷贝到bannerDo中
        * */
        BeanUtils.copyProperties(dto, bannerDO);
        bannerMapper.updateById(bannerDO);

    }

    public PageResponseVO<BannerDO> getBanners(Integer page,Integer count){

        Page<BannerDO> pager = new Page<>(page,count);
        IPage<BannerDO> paging = bannerMapper.selectPage(pager, null);

        return new PageResponseVO<BannerDO>((int)paging.getTotal(), paging.getRecords(), (int)paging.getCurrent(), (int)paging.getSize());

    }

}
JAVA、基础技术、技术与框架SpringBoot系列(二十七)MyBatisPlus联表查询插图3