mybatis一对多时遇到的一个问题(bug)

在mybatis中使用一对多时,在collection中使用select和不使用select两次返回的结果集不同。
在这里插入图片描述
IUserMapper.xml

<?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">

<mapper namespace="com.my.mapper.IUserMapper">
    <resultMap id="userMap" type="user">
        <id property="user_id" column="user_id"></id>
        <result property="user_name" column="user_name"></result>
        <!--从这里开始的两种方式-->
        <collection property="books" column="user_id" ofType="book" select="com.my.mapper.IBookMapper.queryByUserId"></collection>
        <!--<collection property="books" column="user_id" ofType="book" >
            <id property="id" column="id"></id>
            <result property="name" column="name"></result>
            <result property="author" column="author"></result>
            <result property="price" column="price"></result>
            <result property="user_id" column="user_id"></result>
        </collection>-->
        <!--结束-->
    </resultMap>
    <select id="queryAll" resultMap="userMap" >
        select a.user_name,b.* from user a left join book b on a.user_id = b.user_id
    </select>
</mapper>

IBookMapper.xml

<?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">

<mapper namespace="com.my.mapper.IBookMapper">
    <select id="queryByUserId" parameterType="Integer" resultType="book">
        select * from book where user_id = #{userid}
    </select>
</mapper>

TestUser.class

package com.my.test;

import com.my.bean.User;
import com.my.mapper.IUserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class TestUser {
    private InputStream in;
    private SqlSession sqlSession;
    private IUserMapper userMapper;

    @Before
    public void init(){
        try {
            in = Resources.getResourceAsStream("Mybatis_config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession();
            userMapper = sqlSession.getMapper(IUserMapper.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @After
    public void destroy(){
        try {
            sqlSession.close();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testQueryAll(){
        List<User> users = userMapper.queryAll();
        System.out.println(users.size());
        for (User user :
                users) {
            System.out.println(user);//这里两个表虽然是关联的,但是打印对象是分开的
            System.out.println(user.getBooks());
        }
    }
}

先用collection中的select属性,输出:
在这里插入图片描述
现在用另一种方式运行,就是不用select的方式,输出:
在这里插入图片描述
  结果发现两次运行的结果不一样,但是在一对一的关联关系中,这两种方式的运行结果一样。
这到底是怎么回事呢?
  原来是MyBatis为了降低内存开销,采用ResultHandler逐行读取的JDBC ResultSet结果集的,这就会造成MyBatis在结果行返回的时候无法判断以后的是否还会有这个id的行返回,所以它采用了一个方法来判断当前id的结果行是否已经读取完成,从而将其加入结果集List,这个方法是:
  1. 读取当前行记录A,将A加入自定义Cache类,同时读取下一行记录B。
  2. 使用下一行记录B的id列和值为key(这个key由resultMap的标签列定义)去Cache类里获取记录。
  3.假如使用B的key不能够获取到记录,则说明B的id与A不同,那么A将被加入到List。
  4.假如使用B的key可以获取到记录,说明A与B的id相同,则会将A与B合并(相当于将两个goodsImg合并到一个List中,而goods本身并不会增加)。
  5. 将B定为当前行,同时读取下一行C,重复1-5,直到没有下一行记录。
  6. 当没有下一行记录的时候,将最后一个合并的resultMap对应的java对象加入到List(最后一个被合并goodsImg的Goods)。
  所以,
   a. 当结果行是乱序的,例如BBAB这样的顺序,在记录行A遇到一个id不同的曾经出现过的记录行B时, A将不会被加入到List里(因为Cache里已经存在B的id为key的cahce了)。
   b. 当结果是顺序时,则结果集不会有任何问题,因为 记录行 A 不可能 遇到一个曾经出现过的 记录行B, 所以记录行A不会被忽略,每次遇到新行B时,都不可能使用B的key去Cache里取到值,所以A必然可以被加入到List。

所以,使用一对多或者是多对多的时候,最好是手动配置collection,而不要去使用select。

参考自:http://www.360doc.com/content/14/1205/16/15272201_430624495.shtml

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值