Mybatis一对多查询的子对象集合中对象数目不对

来源:互联网 发布:c语言函数求最小公倍数 编辑:程序博客网 时间:2024/05/18 02:43

笔者在初学项目的一对多查询中遇到过两种子对象数目异常情况(一对多查询结果子对象属性项无数据时却显示一条,AND子对象有多个时只显示一条)
《一》在进行一对多查询时,有时会出现当主表对象(一对多的一)的子表属性项(一对多的多)的数目为0时,查询结果却显示主表对象中有一条子表对象数据(子表对象的其中某一项属性有值,其他各项包括id属性都为null)
原因:
   1.在进行多表关联时,由于子表的某一项属性和主表有关联,在collection中又对子表属性对应的<collection>中的该关联属性进行了赋值(column直接写为对应
主表的column),导致查询结果中主表对象中没有子表对象时却出现一条不存在的子表对象(对象内关联属性被赋值,其他属性为null)

<resultMap id="BaseResultMap" type="com.hofon.object.Package">    <id column="ID" property="id">    <result column="NAME" property="name">    <result column="IMAGE" property="mage">    <collection property = "services" ofType="com.hofon.object.Service">        <id column="SERVICE_ID" property="id">(注意这里的id的column要与主表ID中区分开,在SQL中起别名,否则会把主表ID赋给此属性,查询结果子表会只有一条,且ID为 主表ID)        <result column="SERVICE_NAME" property="serviceName">        <result column="ID" property="packageId">(这是错误写法,这个属性是关联的主表中的ID,但是不能column直接写为主表的ID,这样的话如果package下没有service,也会自动获取到一个service,service中其他属性都为null,只有packageId属性有值,且为主表ID的值,,,要想获取这个字段,字段值应该在子表中获取到,将column属性改为column="PID")    </collection></resultMap><select id="selectPackageWithService" resultMap = "BaseResultMap">select p.ID,p.NAME,s.ID SERVICE_ID,s.NAME SERVICENAME,s.PID from package p left join service s on s.PID = p.ID</select>

假设Package有三个数据记录,分别为A,B,C,其中A下面有两个子对象a1,a2,B下面有一个子对象 b,C下面无子对象,则查询结果会如下

A{id:A,name:ANAME,services:{id:a1,service_Name:a1Name,packageId:A}  {id:a2,service_Name:a2Name,packageId:A}}B{id:B,name:BNAME,services:{id:b,service_Name:b1Name,packageId:B}}C{id:C,name:CNAME,services:{id:null,service_Name:null,packageId:C}}

其中packageC下的service是不存在的,产生的结果冗余,因为resultMap中对其packageId赋值为主表ID,而这个主表的ID是有值的,所以会自动产生一个对象结果。
解决:在子表对象对应的<collection>中关联属性的值不直接赋为主表ID,从子表中获取,将子表中关联属性的column指向查询结果中子表的字段column=
"PID">,
   2.在sql中对子表对象的某一属性进行了赋值或null值处理,使其查询不到结果显示为null时,结果显示为0;
如:
select p.ID,p.NAME,ss.ID,ss.NAME SERVICE_NAME,(case when ssr.USENUM is null then 0 else ssr.USENUM end) as useNumber from package p left join service ss on ss.PID = p.IDleft join service_record ssr on ssr.SID = ss.ID
这里查询套餐下一系列服务的使用次数,关联套餐表package,服务表service,服务使用记录表service_record,
这里对useNum值进行处理,想要使套餐中的服务service,若在使用记录表中没有记录使,已使用次数显示0,但是这样会出现的问题是:若某一package下
并没有service时,会显示一条并不存在的service,其ID和NAME都为null,已使用次数为0;与上述1是一样的道理(此处是在SQL中给并不存在的记录中
的某一属性进行了赋值),结果出现一条并不存在的数据。
   解决:赋值之前先对service的ID进行判断,若ID为null,则结果不显示,定义为null,若Id不为null,且使用记录中无数据,则已使用次数结果显示默认值0
select p.ID,p.NAME,ss.ID,ss.NAME SERVICE_NAME,(case when ss.ID is null then null when ssr.USENUM is null then 0 else ssr.USENUM end) as useNumber from package p left join service ss on ss.PID = p.IDleft join service_record ssr on ssr.SID = ss.ID

《二》一个主表对象里有两个属性,分别不同子对象的List集合,两者之间并无直接关联关系,在进行一对多查询时,返回的结果中两个子对象集合的长度相同,且集合长度都为两集合长度正确结果的乘积,比如A中两个集合属性a1,a2,正确结果为a1中有3个对象,a2有2个对象,结果却显示a1,a2都有6个对象;
(①实体类中已增加两个子对象的List为属性②在Mapper.xml中的resultMap也已配好两个<collection标签>)
原因分析:
首先想到是否在两个子对象中都含有某一相同属性,导致查询出来的结果翻倍,经检查,两个子对象的<colleaction>内<result >中的column并无指向同一字段,
且SQL查询语句中两个子表也没有关联字段;后来经过逐步排查,发现是因为在执行SQL时并没有将主表对象的主键属性<Id>查询出来,因为mybaitis是通过主表
主键<id>来确定一个主表对象的,id没有赋值,导致结果中无法确定两种子对象集合分别属于一个主表对象的不同属性,使子集合数据冗余翻倍;
所以:在进行一对多查询时,特别是主表对象里面有多个不同子表对象的集合时,务必记得,一定要将主表对象的主键(并赋值给<resultMap中的第一个<id标签对应的属性>>)查询出来,所有子对象的id也要赋值,并在<collection>中定义<id >标签属性,并且主表对象的<id> 的column属性和子表<id>的column属性不应该相同


PLUS :若子对象的查询结果一直只显示一项,那么去看看在<resultMap>的配置中子表对象的id是否和主表的指向了同一个;;;;
PLUS2: 关于一对多的关系详解:http://blog.csdn.net/xzm_rainbow/article/details/15336933
原创粉丝点击