使用mybatisPlus的queryWrapper进行子查询(以及传统xml方式做leftjoin)

像多对多的数据表关系,比如检查套餐包含检查组,检查组包含检查项,我要想做一个setmeal对象包含groupList,而gropu有itemList,写mybatis的sql怎么写,而用mybatisPlus在java代码里应该怎么写

对于多对多的数据表关系,在 MyBatis 中,通常会使用三张表来表示。以你提到的检查套餐包含检查组、检查组包含检查项为例,我们可以使用如下三张表来表示:

1. 检查套餐表:t_setmeal,包含检查套餐的基本信息,如套餐名称、价格等等。
2. 检查组表:t_checkgroup,用于表示一个检查套餐中的一个检查组,一个套餐可以对应多个检查组。
3. 检查项表:t_checkitem,用于表示一个检查组中的一个检查项,一个检查组可以对应多个检查项。

下面分别介绍在 MyBatis 和 MyBatis-Plus 中,如何写 SQL 语句来查询检查套餐及其包含的检查组和检查项信息。

## 在 MyBatis 中实现

### 查询检查套餐及其包含的检查组和检查项```xml

<select id="findSetmealById" resultMap="setmealResultMap">
  SELECT s.id, s.name, s.code, s.helpCode, s.sex, s.age, s.price, s.remark, s.attention, s.img,
  g.id AS groupId, g.name AS groupName,
  i.id AS itemId, i.name AS itemName
  FROM t_setmeal s
  LEFT JOIN t_setmeal_checkgroup sc ON s.id = sc.setmeal_id
  LEFT JOIN t_checkgroup g ON sc.checkgroup_id = g.id
  LEFT JOIN t_checkgroup_checkitem gi ON g.id = gi.checkgroup_id
  LEFT JOIN t_checkitem i ON gi.checkitem_id = i.id
  WHERE s.id = #{id}
</select>

<resultMap id="setmealResultMap" type="Setmeal">
  <id property="id" column="id"/>
  <result property="name" column="name"/>
  <result property="code" column="code"/>
  <result property="helpCode" column="helpCode"/>
  <result property="sex" column="sex"/>
  <result property="age" column="age"/>
  <result property="price" column="price"/>
  <result property="remark" column="remark"/>
  <result property="attention" column="attention"/>
  <result property="img" column="img"/>
  <collection property="groupList" ofType="CheckGroup">
    <id property="id" column="groupId" />
    <result property="name" column="groupName" />
    <collection property="itemList" ofType="CheckItem">
      <id property="id" column="itemId" />
      <result property="name" column="itemName" />
    </collection>
  </collection>
</resultMap>

```

上面的 SQL 语句使用了 JOIN 多张表,通过 LEFT JOIN 的方式来查询出检查套餐、检查组和检查项之间的关系。其中 resultMap 定义了映射结果,包含了检查套餐、检查组和检查项的信息,并通过 collection 标签来定义了集合类型的属性,把检查组和检查项映射到检查套餐中。

## 在 MyBatis-Plus 中实现

MyBatis-Plus 提供了比 MyBatis 更加简便的使用方式,可以通过实体对象直接进行 CRUD 操作,无需手写 SQL。以下简述如何通过 MyBatis-Plus 实现上面所述的功能。

### 查询检查套餐及其包含的检查组和检查项

为了方便操作,我们需要创建与实体类对应的 Mapper 接口和 Service 接口,并继承 MyBatis-Plus 提供的基类。以下是针对 t_setmeal 表的代码示例:```java

public interface SetmealMapper extends BaseMapper<Setmeal> {
}

public interface SetmealService extends IService<Setmeal> {
    Setmeal findSetmealById(Long id);
}

@Service
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {

    @Override
    public Setmeal findSetmealById(Long id) {
        Setmeal setmeal = baseMapper.selectById(id);
        List<CheckGroup> groupList = baseMapper.findGroupListBySetmealId(id);
        for (CheckGroup group : groupList) {
            List<CheckItem> checkItemList = baseMapper.findItemListByGroupId(group.getId());
            group.setItemList(checkItemList);
        }
        setmeal.setGroupList(groupList);
        return setmeal;
    }
}

```

在上面的代码中,我们利用了 MyBatis-Plus 提供的 BaseMapper 接口和 ServiceImpl 类,简便地实现了 t_setmeal 表的 CRUD 操作,并且通过自定义方法 findSetmealById 来查询检查套餐及其包含的检查组和检查项信息。findSetmealById 方法中使用了两个自定义方法 findGroupListBySetmealId 和 findItemListByGroupId,分别查询检查套餐包含的检查组和检查组包含的检查项信息。

关于如何实现这两个自定义方法,以下是 MyBatis XML Mapper 的代码示例:```xml

<select id="findGroupListBySetmealId" resultMap="BaseResultMap">
    SELECT g.*
    FROM t_setmeal_checkgroup sc
    LEFT JOIN t_checkgroup g ON sc.checkgroup_id = g.id
    WHERE sc.setmeal_id = #{id,jdbcType=INTEGER}
</select>

<select id="findItemListByGroupId" resultMap="BaseResultMap">
    SELECT i.*
    FROM t_checkgroup_checkitem gi
    LEFT JOIN t_checkitem i ON gi.checkitem_id = i.id
    WHERE gi.checkgroup_id = #{id,jdbcType=INTEGER}
</select>

```

上面的自定义方法使用了两个 SQL 语句,分别查询检查组和检查项的信息,并通过 resultMap 定义了映射结果。

最后,我们通过调用 SetmealService 中的 findSetmealById 方法来查询检查套餐及其包含的检查组和检查项信息。MyBatis-Plus 会自动进行对象的映射操作,将查询结果映射成对应的实体类对象。

好的,以下是使用 MyBatis-Plus 实现的 `findSetmealById` 方法:```java

@Service
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {

    @Autowired
    private CheckGroupMapper checkGroupMapper;
    
    @Autowired
    private CheckItemMapper checkItemMapper;
    
    @Override
    public Setmeal findSetmealById(Long id) {
        Setmeal setmeal = this.getById(id);
        
        // 查询所有关联的检查组对象
        List<CheckGroup> groupList = checkGroupMapper.selectList(new QueryWrapper<CheckGroup>().inSql("id", "select checkgroup_id from t_setmeal_checkgroup where setmeal_id = "+id));
        for (CheckGroup group : groupList) {
            // 查询所有关联的检查项对象
            List<CheckItem> itemList = checkItemMapper.selectList(new QueryWrapper<CheckItem>().inSql("id", "select checkitem_id from t_checkgroup_checkitem where checkgroup_id = "+group.getId()));
            group.setItemList(itemList);
        }
        setmeal.setGroupList(groupList);
        
        return setmeal;
    }
}

```

在上述代码中,我们通过 `this.getById(id)` 方法来查询指定 ID 的检查套餐对象 `setmeal`。然后,我们通过 `checkGroupMapper.selectList` 方法,根据检查套餐与检查组之间的关系表(t_setmeal_checkgroup) 查询所有关联的检查组对象 `groupList`,并通过循环遍历每个检查组对象,再通过 `checkItemMapper.selectList` 方法,根据检查组与检查项之间的关系表(t_checkgroup_checkitem)查询所有关联的检查项对象,然后将检查项列表设置到检查组对象中,最后将检查组列表设置到检查套餐对象中,并返回它。

需要注意的是,在查询检查组和检查项的时候,我们使用了 `inSql` 方法,这是因为我们需要查询所有符合条件的对象,而不是只查询一个,而只使用 `eq` 等简单条件查询方法是无法实现的。

另外,需要在 `SetmealServiceImpl` 类中注入 `CheckGroupMapper` 和 `CheckItemMapper` 接口的实现类,这两个 Mapper 接口用于操作检查组表和检查项表。

如果要使用 MyBatis 实现以上代码逻辑,可以参考我之前回答你的问题时的答案。

好的,那么这里是使用 LambdaQueryWrapper 重写上述 `findSetmealById` 方法的示例代码:```java

@Service
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {

    @Autowired
    private CheckGroupMapper checkGroupMapper;

    @Autowired
    private CheckItemMapper checkItemMapper;

    @Override
    public Setmeal findSetmealById(Long id) {
        Setmeal setmeal = this.getById(id);

        // 使用 LambdaQueryWrapper 查询所有关联的检查组对象
        List<CheckGroup> groupList = checkGroupMapper.selectList(new LambdaQueryWrapper<CheckGroup>()
                .inSql(CheckGroup::getId, "select checkgroup_id from t_setmeal_checkgroup where setmeal_id = " + id));

        // 遍历所有关联的检查组对象并查询所有关联的检查项对象
        groupList.forEach(group -> {
            List<CheckItem> itemList = checkItemMapper.selectList(new LambdaQueryWrapper<CheckItem>()
                    .inSql(CheckItem::getId, "select checkitem_id from t_checkgroup_checkitem where checkgroup_id = " + group.getId()));
            group.setItemList(itemList);
        });

        setmeal.setGroupList(groupList);
        return setmeal;
    }
}

```

在上述代码中,我们在查询检查组和检查项时使用了 LambdaQueryWrapper。具体来说,我们使用 `inSql` 方法来实现子查询,使用 `LambdaQueryWrapper::inSql` 方法可以避免手写 SQL,使代码更加简洁易读。

另外,需要注意的是,在遍历所有关联的检查组对象时,我们使用了 Lambda 表达式进行函数式编程。lambda 表达式可以使代码更加简洁,易于维护。

需要提醒的是,要使用 LambdaQueryWrapper,需要在项目中引入 MyBatis-Plus 的 Lambda 模块。

```xml

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-extension</artifactId>
    <version>${mybatis-plus.version}</version>
</dependency>

```

是的,`inSql()`是MyBatis-Plus中用于实现子查询的API之一。它允许我们嵌套一个子查询,将子查询的结果集作为查询条件之一,从而实现更加灵活和复杂的 SQL 查询。

使用 `inSql()` 时,我们需要先编写子查询语句,将结果集映射到某个字段或实体类中,再将子查询语句作为参数传递给 `inSql()` 方法中。例如:

```java

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id", "select id from role where role_name like '管理员%'");
List<User> userList = userMapper.selectList(wrapper);

```

上述代码中,我们使用 `inSql()` 方法将一个子查询语句嵌套到主查询中。该子查询语句通过 `select id from role where role_name like '管理员%'` 获得一组角色ID,这些角色ID会作为过滤条件传递给 `inSql()` 方法中的第二个参数(即 `id` 字段)。该查询语句的意思是,查询角色名称以“管理员”开头的角色ID。由于 `inSql()` 方法本身不支持参数绑定,我们需要将子查询语句中的参数硬编码到语句中。

值得一提的是,MyBatis-Plus 中还有其他用于实现子查询的 API,如 `apply()`和`exists()`等方法。这些 API 均可用于构建复杂的 SQL 查询,并能帮助我们提升查询效率和减少数据库压力。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>