MybatisPlus二级映射和关联对象ResultMap

文章介绍了在在线教育平台中,如何使用MyBatis进行数据库操作,包括章节和资源的一对多和一对一映射,通过ChapterDto类展示如何在Mapper、Service和Controller层实现查询特定课程的详细结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们的教程中,我们设计了一个课程内容的数据结构,包含章节和相关资源。这种结构非常适合在线教育平台或电子学习系统,其中课程内容需要被组织成不同的章节和子章节,每个子章节可能关联特定的学习资源。

这将是一个很好的示例来展示 MyBatis 中如何使用一对多(<collection>)和一对一(<association>)映射。

一、业务背景

1. 数据库表结构

  1. 章节表 (chapter)
    这张表包含所有章节的信息,其中包括大章节和小章节。大章节作为容器,可以包含多个小章节。
    • id (章节ID)
    • parent_id (父章节ID,用于区分大章节和小章节)
    • name (章节名称)
    • courseId (课程ID)
public class Chapter {
    private Long id;
    private Long parentId;
    private String name;
    private Long courseId;
}
  1. 资源表 (resource)
    这张表包含与小章节相关联的资源信息。
    • id (资源ID)
    • section_id (章节ID,关联到章节表)
    • name (资源名称)
public class Resource {
    private Long id;
    private Long sectionId;
    private String name;
}

2. 需求

要求根据courseId查询出指定课程的信息,包括大章节、小章节、资源,并以一定结构返回,比如

[
    {
        "id": 1,
        "parentId": null,
        "name": "Chapter 1",
        "courseId": 100,
        "subChapters": [
            {
                "id": 11,
                "parentId": 1,
                "name": "Section 1.1",
                "courseId": 100,
                "resource": {
                    "id": 101,
                    "sectionId": 11,
                    "name": "Introduction Video"
                }
            },
            {
                "id": 12,
                "parentId": 1,
                "name": "Section 1.2",
                "courseId": 100,
                "resource": null
            }
        ],
        "resource": null
    }
    // other...
]

所以我们定义一个Dto如下

public class ChapterDto extends Chapter {
    private List<ChapterDto> subChapters;
    private Resource resource;

    // 构造器、getter和setter
}

二、使用映射直接得到指定结构

ChapterMapper.xml 文件中,我们定义 SQL 查询以及结果映射。

<mapper namespace="com.example.mapper.ChapterMapper">

    <resultMap id="ChapterDtoMap" type="com.example.dto.ChapterDto">
        <id column="chapter_id" property="id" />
        <result column="parent_id" property="parentId" />
        <result column="name" property="name" />
        <result column="courseId" property="courseId" />
        <collection property="subChapters" ofType="com.example.dto.ChapterDto">
            <id column="sub_chapter_id" property="id" />
            <result column="sub_parent_id" property="parentId" />
            <result column="sub_name" property="name" />
            <result column="sub_courseId" property="courseId" />
            <association property="resource" javaType="com.example.model.Resource">
                <id column="resource_id" property="id" />
                <result column="section_id" property="sectionId" />
                <result column="resource_name" property="name" />
            </association>
        </collection>
    </resultMap>

    <select id="selectChaptersWithResources" resultMap="ChapterDtoMap">
        SELECT
            c.id AS chapter_id, c.parent_id, c.name, c.courseId,
            sc.id AS sub_chapter_id, sc.parent_id AS sub_parent_id, sc.name AS sub_name, sc.courseId AS sub_courseId,
            r.id AS resource_id, r.section_id, r.name AS resource_name
        FROM
            chapter c
        LEFT JOIN
            chapter sc ON c.id = sc.parent_id
        LEFT JOIN
            resource r ON sc.id = r.section_id
        WHERE
            c.courseId = #{courseId} AND c.parent_id IS NULL
    </select>

</mapper>

三、其他文件

1. Mapper

public interface ChapterMapper {
    List<ChapterDto> selectChaptersWithResources(Long courseId);
}

2. Service

@Service
public class ChapterService {
    @Autowired
    private ChapterMapper chapterMapper;

    public List<ChapterDto> getChaptersWithResources(Long courseId) {
        return chapterMapper.selectChaptersWithResources(courseId);
    }
}

3. Controller

@RestController
@RequestMapping("/chapters")
public class ChapterController {
    @Autowired
    private ChapterService chapterService;

    @GetMapping("/{courseId}")
    public ResponseEntity<List<ChapterDto>> getChapters(@PathVariable Long courseId) {
        List<ChapterDto> chapters = chapterService.getChaptersWithResources(courseId);
        return ResponseEntity.ok(chapters);
    }
}

四、概念理解

一级映射

在提供的 resultMap 中,一级映射是针对 ChapterDto类的直接属性的映射。这意味着数据库中的列(如 chapter_id, parent_id等)直接映射到 ChapterDto类的相应属性(如 id, parent_id等),这部分映射是非常直接的。

二级映射

二级映射用于处理复杂的对象关系,比如当一个对象包含其他对象或对象的集合时。这通常在处理一对多关系时出现,例如,一个章节结构(ChapterDto)可能包含多个子章节。

聚合

这种聚合是根据您在 <collection> 标签中定义的规则进行的。MyBatis 会识别哪些行应该被映射为独立的实例,哪些行应该作为子元素聚合到其他实例中。

五、标签使用

1. <collection> 标签

用途:用于映射一对多关系。在这个例子中,ChapterDto类包含一个 Chapter 类型的列表,这代表了大章节和小章节之间的一对多关系。

常用属性

  • property:指定要映射到的目标属性名称。
  • ofType:指定集合中元素的类型。

2. <association> 标签

用途:用于映射一对一关系。在您的例子中,ChapterDto包含一个 Resource 类型的属性,这代表了小章节和资源之间的一对一关系。
常用属性

  • property:指定要映射到的目标属性名称。
  • javaType:指定关联对象的类型。

在这里插入图片描述

MybatisPlus是一个基于Mybatis框架的增强工具,可以简化加速开发过程。在使用MybatisPlus进行数据库操作时,我们可以利用它自带的功能来进行基本的CRUD操作,同时也可以进行自定义的映射resultmap。 自定义映射resultmap可以帮助我们在数据库查询结果与实体类之间建立映射关系,进而方便地操作数据。一般情况下,MybatisPlus会根据数据库字段与实体类属性的对应关系自动进行映射,但在某些特殊情况下,我们可能需要自己来定义映射关系。 要自定义映射resultmap,我们可以通过在实体类中使用注解`@ResultMap`来实现。首先,我们需要在实体类中定义与数据库字段对应的属性,并通过注解`@TableField`来指定数据库字段名。然后,我们可以在实体类中使用注解`@ResultMap`来声明自定义的映射resultmap,并指定与数据库字段对应的属性。 例如,有一个数据库表student,包含字段id、nameage,我们可以定义一个实体类Student,其中id属性对应数据库字段id,name属性对应数据库字段name,age属性对应数据库字段age。然后,在Student类中使用注解`@ResultMap`自定义映射resultmap,例如: ```java @Data public class Student { @TableField("id") private Long id; @TableField("name") private String name; @TableField("age") private Integer age; @ResultMap("studentResultMap") public class StudentResultMap{ return new StudentResultMap(){ put("id", "id"); put("name","name"); put("age","age"); } } } ``` 在上述示例中,我们定义了一个名为studentResultMap的自定义映射resultmap,并通过`put`方法指定了属性与数据库字段的对应关系。 当我们需要进行数据库查询时,可以通过`@Select`注解或者使用MybatisPlus提供的查询方法来执行查询操作。在查询操作中,我们可以使用自定义映射resultmap来获取查询结果,并将其映射到实体类中。 总之,通过自定义映射resultmap,我们可以灵活地实现数据库查询结果与实体类的映射关系,提高了系统的可维护性代码的可读性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值