Mybatis 多对多映射的小问题

回想到一句别走得多么远离

Posted by MatthewHan on 2019-12-26

问题

ORM映射中,一对多,多对多是非常常见的方式。但是由于场景使用没有这么多,到今天我才发现这个问题。

配置映射关系,使字段与DO类解耦,方便维护。但是为了方便管理和控制,我就在一个.java文件中写了两个VO类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@ToString
@Setter(value = AccessLevel.PUBLIC)
@Getter(value = AccessLevel.PUBLIC)
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class CategoryVO {

private Long cid;
private Long pid;
private Integer level;
private Integer sortNo;
private String cname;
private java.sql.Timestamp created;
private java.sql.Timestamp updated;

private List<Word> words;
}
@ToString
@Setter(value = AccessLevel.PUBLIC)
@Getter(value = AccessLevel.PUBLIC)
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(value = JsonInclude.Include.NON_NULL)
class Word {
private Long id;
private Long hotWordId;
private Long categoryId;
private String keyword;
private Integer source;
private Integer sortNo;
private Integer isDeleted;
private java.sql.Timestamp created;
private java.sql.Timestamp updated;
}

它们的关系也非常简单,可以直接看出,一个类目(分类)对应多组HotWord(热词)。所以在resultMap中为了图快我就直接把属性一个个映射数据库的column了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<resultMap id="result" type="{hape}.common.orm.model.vo.CategoryVO">
<!-- 相同的列名id -->
<id column="id" jdbcType="INTEGER" property="cid"/>
<result column="parent_id" jdbcType="INTEGER" property="pid"/>
<result column="name" jdbcType="VARCHAR" property="cname"/>
<result column="level" jdbcType="INTEGER" property="level"/>
<result column="sort_no" jdbcType="INTEGER" property="sortNo"/>
<collection property="words" ofType="{hape}.common.orm.model.vo.Word">
<!-- 相同的列名id -->
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="hotword_id" jdbcType="INTEGER" property="hotWordId"/>
<result column="category_id" jdbcType="INTEGER" property="categoryId"/>
<result column="keyword" jdbcType="VARCHAR" property="keyword"/>
<result column="source" jdbcType="TINYINT" property="source"/>
<result column="sort_no" jdbcType="INTEGER" property="sortNo"/>
<result column="is_deleted" jdbcType="TINYINT" property="isDeleted"/>
<result column="created" jdbcType="TIMESTAMP" property="created"/>
<result column="updated" jdbcType="TIMESTAMP" property="updated"/>
</collection>
</resultMap>

但是实际结果却并不是一个包含了多个子结构的对象,而是只有一个words对象,究其原因,原来是因为colum列名有重复,都存在一个相同的id。

解决办法

你可以选择干掉其中一个id,或者在使用SQL语句中加入别名加以区分,当字段较多的时候就会比较麻烦。我这里把字段都加上了w_区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<select id="selectHotWordByCategoryId" parameterType="int" resultMap="result">
select
<!--为什么要as "w_"呢?-->
<!--因为两张表的colum有相同的名称会出现问题,不然就只能查出子表的一条而不是多条了-->
t1.*, t2.id as w_id, t2.hotword_id as w_hotword_id,
t2.keyword as w_keyword, t2.source as w_source,
t2.sort_no as w_sort_no, t2.created as w_created,
t2.updated as w_updated
from
xunfei_category t1
left join
xunfei_hotword t2
on
t1.id = t2.category_id
where
t1.id = #{id}
and
t2.is_deleted = 0
order by
t2.sort_no,
t2.updated desc
</select>

然后在resultMap中修改成改后的别名就OK啦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<resultMap id="result" type="{hape}.common.orm.model.vo.CategoryVO">
<id column="id" jdbcType="INTEGER" property="cid"/>
<result column="parent_id" jdbcType="INTEGER" property="pid"/>
<result column="name" jdbcType="VARCHAR" property="cname"/>
<result column="level" jdbcType="INTEGER" property="level"/>
<result column="sort_no" jdbcType="INTEGER" property="sortNo"/>
<collection property="words" ofType="{hape}.common.orm.model.vo.Word">
<id column="w_id" jdbcType="INTEGER" property="id"/>
<result column="w_hotword_id" jdbcType="INTEGER" property="hotWordId"/>
<result column="w_category_id" jdbcType="INTEGER" property="categoryId"/>
<result column="w_keyword" jdbcType="VARCHAR" property="keyword"/>
<result column="w_source" jdbcType="TINYINT" property="source"/>
<result column="w_sort_no" jdbcType="INTEGER" property="sortNo"/>
<result column="w_is_deleted" jdbcType="TINYINT" property="isDeleted"/>
<result column="w_created" jdbcType="TIMESTAMP" property="created"/>
<result column="w_updated" jdbcType="TIMESTAMP" property="updated"/>
</collection>
</resultMap>