MyBatis,collection标签和association标签的区别啥?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis,collection标签和association标签的区别啥?相关的知识,希望对你有一定的参考价值。

比如同时有A.java和B.java两个类,A.java如下:
public class A
private B b1;
private List<B> b2;

在映射b1属性时用association标签, 映射b2时用collection标签,分别是一对一,一对多的关系
参考技术A

1.比如同时有A.java和B.java两个类,A.java如下:

public class A
    private B b1;
    private List<B> b2;

2.一对一:关联对象(如:User)或者一个属性(如:name) , 用 association

3.一对多:关联一个集合(如:userList) , 用 collection

mybatis一对多查询<association;和<collection;标签踩坑

需求为:将视图返回的查询结果(一对多关联查询)映射到实体类中。

最开始的错误写法:

创建视图(已知a为主表,b为子表,主子表为一对多关系):

CREATE VIEW `view_xxx` as
select
a.id as a_id,
a.name as a_name,
b.id as b_id,
b.aid as b_aid,
b.name as b_name
from a
left join b on b.aid=a.id;

实体:

public class Result{

    private A a;

    private List<B> bs;

}
public class A {

  private String id; private String name; }
public class B {

  private String id; private String aid; private String name; }

Repository文件:

public interface ResultRepository {

    Result selectOneById(@Param("id") String id);

}

Mapper.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="ResultRepository">
    <resultMap id="ResultMap" type="Result">
        <association property="a" javaType="A">
            <id     property="id" column="a_id" />
            <result property="name" column="a_name" jdbcType="VARCHAR"/>
        </association>
        <collection property="bs" ofType="B">
            <id     property="id" column="b_id" />
            <result property="aid" column="b_aid" jdbcType="VARCHAR"/>
            <result property="name" column="b_name" jdbcType="VARCHAR"/>
        </collection>
    </resultMap>

    <select id="selectOneById" resultMap="ResultMap">
        select * from view_xxx where a.id = #{id}
    </select>

</mapper>

万事俱备,重启服务,调用,结果抛出了TooManyResultsException异常:

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:81)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at 

但直接在数据库执行sql(select * from view_xxx where a.id = xxx),确实是只有一条主表数据对应多条子表数据的,而且是使用主表主键做where条件查询的啊

于是我又使用List类型来接收查询结果:

//之前调用的代码
Result result = resultRepository.selectOneById(id);
//之后调用的代码
List<Result> results = resultRepository.selectOneById(id);

果然返回的结果为多条(子表数据数量),且每条数据的主表数据都相同。

那么问题来了,既然主表的数据都相同,为什么没有将子表数据封装到集合中呢?

我在网上查了很久也没有找到与我类似的问题,而且我发现我的Mapper文件中确实没有指出哪个字段为关联键。到这里问题似乎陷入了死胡同

最后我发现,网上其他人的Mapper文件中的<resultMap>标签中,除<association>和<collection>标签外,都有其他标签(<id>或<result>)

问题会不会出在这上面呢,于是我修改了视图(此处只修改增加了返回字段a.id as id):

CREATE VIEW `view_xxx` as
select
a.id as id, a.id as a_id, a.name as a_name, b.id as b_id, b.aid as b_aid, b.name as b_name from a left join b on b.aid=a.id;

修改了Result实体(A、B实体未修改):

public class Result{

    private String id;

    private A a;

    private List<B> bs;

}

修改了Mapper.xml文件(增加了<id property="id" column="id" />):

<?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="ResultRepository">
    <resultMap id="ResultMap" type="Result">
        <id     property="id" column="id" />
        <association property="a" javaType="A">
            <id     property="id" column="a_id" />
            <result property="name" column="a_name" jdbcType="VARCHAR"/>
        </association>
        <collection property="bs" ofType="B">
            <id     property="id" column="b_id" />
            <result property="aid" column="b_aid" jdbcType="VARCHAR"/>
            <result property="name" column="b_name" jdbcType="VARCHAR"/>
        </collection>
    </resultMap>

    <select id="selectOneById" resultMap="ResultMap">
        select * from view_xxx where a.id = #{id}
    </select>

</mapper>    

再次重启、调试,问题解决。

个人总结:

当resultMap中有主键时,将根据相同主键值自动合并到collection中。

而第一次调试时由于没有主键字段,只有一个自定义实体属性。所以未能正确合并List。

至此,需求已实现。但仍未在网上找到关于此问题相关的正统知识点讲解。

若有知道的大佬,或发现文章有哪些问题的小伙伴,可给我评论留言。大家一起进步吧~

以上是关于MyBatis,collection标签和association标签的区别啥?的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis之ResultMap的association和collection标签详解(图文例子)

Mybatis中 collection 和 association 的区别

Mybatis之collection与association标签

mybatis一对多查询<association;和<collection;标签踩坑

MyBatis从入门到精通:使用collection标签实现嵌套查询

MyBatis(映射文件中使用foreach标签时报错,属性collection的问题)