文章目录
- 一、业务背景
- 1. 数据库表结构
- 2. 需求
- 二、使用映射直接得到指定结构
- 三、其他文件
- 1. Mapper
- 2. Service
- 3. Controller
- 四、概念理解
- 一级映射
- 二级映射
- 聚合
- 五、标签使用
- 1. \<collection\> 标签
- 2. \<association\> 标签
在我们的教程中,我们设计了一个课程内容的数据结构,包含章节和相关资源。这种结构非常适合在线教育平台或电子学习系统,其中课程内容需要被组织成不同的章节和子章节,每个子章节可能关联特定的学习资源。
这将是一个很好的示例来展示 MyBatis 中如何使用一对多(<collection>
)和一对一(<association>
)映射。
一、业务背景
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;
}
- 资源表 (
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
:指定关联对象的类型。