mybatis一对多查询<association;和<collection;标签踩坑
Posted 陈婉兮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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一对多查询<association;和<collection;标签踩坑的主要内容,如果未能解决你的问题,请参考以下文章