Springboot2.x 使用 MyBatis 实现 Mysql 一对一,一对多关联、通过中间表查询第三表和多层嵌套查询

参考

1.Mybatis3 中文文档

疑惑

MyBatis关联查询有两种方式,1. 嵌套 Select 查询 2. 嵌套结果映射,使用1方式进行查询就是相当于执行一个sql语句,很容易理解。但是2的方式不需要指定表名,也不需要指定关联的表名,但是可以查出来你想要的数据,并且解决N+1的问题。但是2的方式不知道怎么去搜索关键字查询为什么不用指定表名和关联的表名。

因为以上疑惑问题,所以本文内容只有嵌套 Select 查询 示例。

写了很多代码示例,发现也写不明白,直接贴代码吧。

代码片段

association – 一个复杂类型的关联;许多结果将包装成这种类型(一对一使用)
嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用

collection – 一个复杂类型的集合(一对多使用)
嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用

  1. 一对一查询,通过查询findById,查询出指定id的文章信息后,再通过返回类型resultMap去执行resultMap id='ArticleAndLabelMapper'的关联,再通过设置 id 进行标识唯一映射(不设置有肯能影响性能),通过 collection 的 select 执行查询文章的第一个标签(select不仅仅可以指定本xml映射文件内的语句,还可以指定其他文件内的语句,如果是本文件内的就直接 select=id ;不是本文件内的就使用 select=指定文件的mapper映射类路径+id)。
    <resultMap id="ArticleAndLabelMapper" type="com.xxx.blog.entity.ArticleEntity">
        <!--- 标识唯一,有助于性能 -->
		<id property="id" column="id" />
		<!--- collection:应用于一对一-->
		<!--- property:标识映射到文章实体中的属性名,column:代表传入给select的参数,支持多个参数如 column="{prop1=col1,prop2=col2}" 这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 select :标识要执行的语句,可以是其他文件中的,但是要在Mapper中添加映射才能使用,否则报错-->
        <association property="labels" column="id" select="com.xxx.blog.mapper.ArticleLabelMapper.findByArticleIdFirstLabel">
        </association>
    </resultMap>
    <select id="findById" parameterType="int" resultMap="ArticleAndLabelMapper" >
        select * from `articles` where id = #{id}
    </select>
  1. 一对多查询,通过查询findById,查询出指定id的文章信息后,再通过返回类型resultMap去执行resultMap id='ArticleAndLabelMapper'的关联,再通过设置 id 进行标识唯一映射(不设置有肯能影响性能),通过 collection 的 select 执行查询文章的标签列表(select不仅仅可以指定本xml映射文件内的语句,还可以指定其他文件内的语句,如果是本文件内的就直接 select=id ;不是本文件内的就使用 select=指定文件的mapper映射类路径+id)。
    <resultMap id="ArticleAndLabelMapper" type="com.xxx.blog.entity.ArticleEntity">
         <!--- 标识唯一,有助于性能 -->
        <id property="id" column="id" />
		 <!--- collection:应用于一对多-->
		<!--- property:标识映射到文章实体中的属性名,column:代表传入给select的参数,支持多个参数如 column="{prop1=col1,prop2=col2}" 这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 select :标识要执行的语句,可以是其他文件中的,但是要在Mapper中添加映射才能使用,否则报错-->
        <collection property="labels" column="id" select="com.xxx.blog.mapper.ArticleLabelMapper.findByArticleIdLabels">
        </collection>
    </resultMap>
	<!--- 查询全部文章后执行id=ArticleAndLabelMapper 的 resultMap 映射语句 -->
    <select id="findById" parameterType="int" resultMap="ArticleAndLabelMapper" >
        select * from `articles` where id = #{id}
    </select>
  1. 通过文档说明 嵌套结果映射 – 集合可以是 resultMap 元素,或是对**其它结果映射的引用**,可以实现多层查询,如查询指定文章后通过resultMap 查询到文章的标签表,可以在 resultMap 指定的 select 语句中再返回 resultMap 实现多层查询。

  2. 一个简单的一对多,以及通过中间表实现关联查询第三表的功能。以及增删改查代码示例,仅供自己参考

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--这里对应Maper接口类的命名空间-->
<mapper namespace="com.xxx.blog.mapper.ArticleMapper">
    <!--    这里 resultType 对应响应实体类,也可以是其他类型,请参考文档-->
<!--    <select id="findById" parameterType="int" resultType="com.xxx.blog.entity.ArticleEntity">-->
    <resultMap id="ArticleAndLabelMapper" type="com.xxx.blog.entity.ArticleEntity">
        <id property="id" column="id" />
        <collection property="labels" column="id" select="com.xxx.blog.mapper.ArticleLabelMapper.findByArticleIdLabels">
        </collection>
    </resultMap>
    <select id="findById" parameterType="int" resultMap="ArticleAndLabelMapper" >
        select * from `articles` where id = #{id}
    </select>
    <select id="findByPaging" resultMap="ArticleAndLabelMapper" parameterType="map">
        select
        id,title,introduction,created_at,updated_at,shows,likes
        from `articles` order by id desc
    </select>
    <insert id="add" useGeneratedKeys="true"
            keyProperty="id" parameterType="com.xxx.blog.entity.ArticleEntity">
        insert into articles (title, introduction,html,markdown,created_at,updated_at) values(#{title}, #{introduction},#{html},#{markdown},#{createdAt},#{updatedAt})
    </insert>
    <update id="update" useGeneratedKeys="true" keyProperty="id" parameterType="com.xxx.blog.entity.ArticleEntity">
        update articles set
                title = #{title}, introduction = #{introduction}, html=#{html}, markdown=#{markdown}, updated_at = #{updatedAt}
        where id = #{id}
    </update>
    <delete id="delete" parameterType="int">
        delete from articles where id = #{id}
    </delete>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--这里对应Maper接口类的命名空间-->
<mapper namespace="com.xxx.blog.mapper.ArticleLabelMapper">
    <!--    根据文章id关联查询类别,这里相当于第二表查询第三表-->
    <select id="findByArticleIdLabels" parameterType="int" resultType="com.xxx.blog.entity.LabelEntity">
        select labels.id,labels.name from `article_labels` join `labels` on labels.id = article_labels.label_id where article_labels.article_id = #{articleId}
    </select>
<!--    添加多个,添加多个的时候就不能获取刚刚插入的id了,因为他是个列表-->
    <insert id="addArticleLabels">
        insert into `article_labels` (article_id, label_id) values
        <foreach item="labelId" index="index" collection="param2"
                 separator=",">
            (#{param1}, #{labelId})
        </foreach>
    </insert>
<!--    删除该文章所有-->
    <delete id="deleteArticleLabels" parameterType="int">
        delete from article_labels where article_id = #{articleId}
    </delete>
</mapper>
  1. 一个比较复杂的一对一及一对多的代码示例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--这里对应Maper接口类的命名空间-->
<mapper namespace="com.xxx.suddenlynlinelearningplatform.mapper.VideoCommentMaper">
<!--  查询一级评论关联信息和他的孩子们  -->
     <resultMap type="com.xxx.suddenlynlinelearningplatform.entity.VideoComment" id="videoCommentInfo">
         <id property="id" column="id"/>
         <association property="myLike" column="{videoCommentId=id,studentId=my_status_id}"  select="com.xxx.suddenlynlinelearningplatform.mapper.VideoCommentLikeMaper.findMyIsLike">
         </association>
         <association property="student" column="student_id" select="com.xxx.suddenlynlinelearningplatform.mapper.StudentMaper.findVideoCommentStudentById">
         </association>
         <association property="repliedStudent" column="replied_student_id" select="com.xxx.suddenlynlinelearningplatform.mapper.StudentMaper.findVideoCommentStudentById">
         </association>
         <collection property="videoCommentList" column="{videoCommentId=id,myStatusId=my_status_id}" select="findByWhileClassVideoComments">
         </collection>
     </resultMap>
<!--  一级评论  -->
    <select id="findByFirstLevelVideoComments" resultMap="videoCommentInfo">
        select *, #{param2} as my_status_id from `video_comments` where video_comment_id=0 and status = 1 and video_id=#{param1}
    </select>
<!--  二级评论  -->
    <select id="findByWhileClassVideoComments" resultMap="videoCommentInfo">
        select *, #{myStatusId} as my_status_id from `video_comments` where video_comment_id=#{videoCommentId}
    </select>
<!--    插入-->
    <insert id="add" useGeneratedKeys="true"
            keyProperty="id" parameterType="com.xxx.suddenlynlinelearningplatform.entity.VideoComment">
        insert into `video_comments`
            (video_id,video_comment_id,student_id,replied_student_id,content,likes,status,created_at)
            values
            (
             #{videoId},
             #{videoCommentId},
             #{studentId},
             #{repliedStudentId},
             #{content},0,1,#{createdAt})
    </insert>
</mapper>
posted @   夏秋初  阅读(531)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
历史上的今天:
2019-08-26 Laravel 表单验证创建“表单请求”实现自定义请求类
点击右上角即可分享
微信分享提示