Mybatis关联查询

一对一关联
创建评论表 posts 和 博客表 blog ,一个blog下有多个评论,每条评论都说针对一条博客。所以每一条评论和博客的关系是一对一。每个博客和评论的关系是 一对多,建表语句如下:

CREATE TABLE posts (
pid int(11) NOT NULL AUTO_INCREMENT,
post_name varchar(45) DEFAULT NULL,
blog_id int(11) DEFAULT NULL,
PRIMARY KEY (pid)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

CREATE TABLE blog (
bid int(11) NOT NULL AUTO_INCREMENT,
name varchar(45) COLLATE utf8_bin DEFAULT NULL,
author_id int(11) DEFAULT NULL,
PRIMARY KEY (bid)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

Post.java 中持有一个Blog blog 属性 ,Blog.java 中持有一个 List posts属性如下:
在这里插入图片描述
在这里插入图片描述

PostMapper.xml 中 增加如下两种查询:嵌套结果(getPost)和嵌套查询(getPost2)

  <!--
     方式一:嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集
             封装联表查询的数据(去除重复的数据)
     -->
  <!-- 使用resultMap映射实体类和字段之间的一一对应关系 -->
  <resultMap id="UnionResultMap" type="com.test.mybatis.beans.Post">
    <id column="pid" jdbcType="INTEGER" property="pid" />
    <result column="post_name" jdbcType="VARCHAR" property="postName" />
    <result column="blog_id" jdbcType="INTEGER" property="blogId" />
    <association property="blog" javaType="com.test.mybatis.beans.Blog">
      <id property="bid" column="bid"/>
      <result property="name" column="name"/>
    </association>
  </resultMap>
  <select id="getPost" parameterType="int" resultMap="UnionResultMap">
        select * from posts p,blog b where b.bid=p.blog_id  and p.pid=#{id}
  </select>
  
  <!--
   方式二:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型
         select * from posts p where  p.pid=#{id}
        select * from blog b where  b.bid=#{id}

     -->
  <resultMap id="UnionResultMap2" type="com.test.mybatis.beans.Post">
    <id column="pid" jdbcType="INTEGER" property="pid" />
    <result column="post_name" jdbcType="VARCHAR" property="postName" />
    <result column="blog_id" jdbcType="INTEGER" property="blogId" />
    <!--使用resultMap映射实体类和字段之间的一一对应关系-->
    <association property="blog" column="blog_id" select="getBlog">
    </association>
  </resultMap>
  
  <select id="getPost2" parameterType="int" resultMap="UnionResultMap2">
        select * from posts p where  p.pid=#{id}
  </select>
  <select id="getBlog" parameterType="int" resultType="com.test.mybatis.beans.Blog">
        select * from blog b where  b.bid=#{id}
  </select>

PostMapper.java 中增加 对应的查询方法

Post getPost(Integer pid);
Post getPost2(Integer pid);

测试代码:

SqlSession session = getSqlSession();
        PostMapper mapper = session.getMapper(PostMapper.class);
        Post post =mapper.getPost(1);
        System.out.println(post.getBlog().getName());
        Post post2 =mapper.getPost2(1);
        System.out.println(post2.getPostName());
        System.out.println(post2.getBlog().getName());

执行结果可可以看到,嵌套结果一次查询出结果字段并将结果赋值到指定对象中,嵌套查询的方式 执行了两个sql 获取信息:
在这里插入图片描述
总结:
MyBatis中使用association标签来解决一对一的关联查询,association标签可用的属性如下:
property:对象属性的名称
javaType:对象属性的类型
column:所对应的外键字段名称
select:指定一个查询来获取结果

一对多关联
继续以上边示例 ,查询blog时 blog 对post 是一对多。
BlogMapper.xml 中增加如下两种查询实现(嵌套结果和嵌套查询)

  <resultMap id="UnionResultMap" type="com.test.mybatis.beans.Blog">
    <id column="bid" jdbcType="INTEGER" property="bid" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="author_id" jdbcType="INTEGER" property="authorId" />
    <collection  property="posts" ofType="com.test.mybatis.beans.Post">
      <id  column="pid" property="pid" ></id>
      <result  column="postName" property="post_name" ></result>
    </collection >
  </resultMap>
  <!--方式一: 嵌套结果: 使用嵌套结果映射来处理重复的联合结果的子集-->
  <select id="getBlog" parameterType="int" resultMap="UnionResultMap">
    select * from posts p,blog b where b.bid=p.blog_id  and b.bid=#{id}
   </select>
  
 <resultMap id="UnionResultMap2" type="com.test.mybatis.beans.Blog">
  <id column="bid" jdbcType="INTEGER" property="bid" />
  <result column="name" jdbcType="VARCHAR" property="name" />
  <result column="author_id" jdbcType="INTEGER" property="authorId" />
  <collection  property="posts" column="bid" ofType="com.test.mybatis.beans.Post" select="getPosts">
  </collection >
</resultMap>
  <select id="getBlog2" parameterType="int" resultMap="UnionResultMap2">
    select b.bid,b.name,b.author_id from blog b where  b.bid=#{id}
   </select>
  <select id="getPosts" parameterType="int" resultType="com.test.mybatis.beans.Post">
    select p.pid,p.post_name,p.blog_id from posts p where  p.blog_id=#{id}
   </select>

BlogMapper.java 中增加相应查询方法

Blog getBlog(Integer bid);
Blog getBlog2(Integer bid);

测试代码:

    BlogMapper bmapper = session.getMapper(BlogMapper.class);
    Blog b = bmapper.getBlog(1);
    System.out.println(b.getName());
    System.out.println(b.getPosts().size());

    Blog b2 = bmapper.getBlog2(1);
    System.out.println(b2.getName());
    System.out.println(b2.getPosts().size());

执行结果如下,可以看到嵌套结果执行一条sql 并将结果封装到了对应属性中.嵌套查询方式通过多个查询获取结果。

在这里插入图片描述
总结:
MyBatis中使用collection标签来解决一对多的关联查询,ofType属性指定集合中元素的对象类型。

N+1问题:
对于上述示例中的嵌套查询的方式,比如我们调用getBlog2查询Blog信息时,这个时候不管我们是不是需要使用评论信息(posts属性),Mybatis 都会执行一条sql把它查询出来。如果我们的Blog 还有其他的关联信息(比如 作者,分类等属性),也会一次性全部查询出来,但是我们可能根本不需要。这就造成了性能的浪费。就是Mybatis 的 N+1(1条sql+N条关联信息sql)问题。克服N+1问题的方法就是延迟加载

Mybatis 配置文件中增加如下配置

 <setting>
            <setting name="lazyLoadingEnabled" value="true"/>
            <setting name="aggressiveLazyLoading" value="false"/>
   </setting>

执行如下测试代码

  BlogMapper bmapper = session.getMapper(BlogMapper.class);
        Blog b2 = bmapper.getBlog2(1);
        System.out.println(b2.getName());

只执行了一条sql 并未执行关联的sql ,关联的sql查询只在需要的时候才回去执行。
在这里插入图片描述

配置说明:
lazyLoadingEnabled:延迟加载的全局开关,当开启时,所有关联都会延迟加载。在特定的关联中,使用fetchType属性覆盖该内容的功能。
aggressiveLazyLoading:是层级延迟加载开关,就是处于同一个层级的关联表会同时延迟加载,或者同时被加载
lazyLoadingEnabled 和 aggressiveLazyLoading 都是全局配置,全局配置让全局都适用统一的规则,不同业务或功能对延迟加载的需求不一样,所以只配置全局的开关可能会影响部分功能的性能。

fetchType属性解决全局配置的缺点。fetchType出现在级联元素association,collection中,它存在两个值
- eager:获得当前POJO后立即加载对应的数据。
- lazy:获得当前POJO后延迟加载对应的数据。

我们在BlogMapper.xml 中 的collection 元素中 配置 fetchType=‘eager’ 如下:

在这里插入图片描述
再次执行测试代码结果延迟加载失效,直接执行了两条sql查询出了 评论信息:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

catch that elf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值