Mybatis中resultMap如何使用
Mybatis中resultMap如何使用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
一、字段映射
在Mybatis中,最简单的结果映射方式,就是通过类型别名typeAliases来处理。
如果要这样做,那么第一步需要配置实体类包的路径:
mybatis.type-aliases-package=com.xxx.entity
该路径下的所有类,就会被注册到TYPE_ALIASES容器。我们在指定返回值类型的时候,就直接用别名即可。
比如,我们有一个User类:
@Data publicclassUser{ privateStringid; privateStringusername; privateStringpassword; privateStringaddress; privateStringemail; }
如果数据库中表的字段与User类的属性名称一致,我们就可以使用resultType来返回。
<selectid="getUsers"resultType="User"> SELECT u.id, u.username, u.password, u.address, u.email FROM USERu </select>
当然,这是理想状态下,属性和字段名都完全一致的情况。但事实上,不一致的情况是有的,这时候我们的resultMap就要登场了。
如果User类保持不变,但SQL语句发生了变化,将id改成了uid。
<selectid="getUsers"resultType="User"> SELECT u.idasuid, u.username, u.password, u.address, u.email FROM USERu </select>
那么,在结果集中,我们将会丢失id数据。这时候我们就可以定义一个resultMap,来映射不一样的字段。
<resultMapid="getUserByIdMap"type="User"> <resultproperty="id"column="uid"></result> </resultMap>
然后,我们把上面的select语句中的resultType修改为resultMap="getUserByIdMap"
。
这里面column对应的是数据库的列名或别名;property对应的是结果集的字段或属性。
这就是resultMap最简单,也最基础的用法:字段映射。
下面,我们看看其他几种标签都是怎么应用的。
元素名称 | 描述 |
---|---|
constructor | 用于在实例化类时,注入结果到构造方法中 |
association | 关联一个对象 |
collection | 关联多个对象 |
二、构造方法
如果你希望将结果注入构造方法里,就可以用到constructor元素。
比如,我们的User类增加了一个构造方法:
publicUser(Stringid,Stringname){ this.id=id+"--------"; this.username=name+"--------"; }
我们需要在resultMap中定义constructor元素:
<resultMapid="getUserByIdMap"type="User"> <constructor> <idArgcolumn="id"name="id"javaType="string"></idArg> <argcolumn="username"name="name"javaType="string"></arg> </constructor> </resultMap>
其中,column代表数据库字段名称或者别名;name则是构造方法中的参数名称;javaType指定了参数的类型。
如你所想,这样指定构造方法后,我们结果集中的id和username属性都会发生变化。
{ "id":"1001--------", "username":"后羿--------", "password":"123456", "address":"北京市海淀区", "email":"510273027@qq.com" }
三、关联
在实际的业务中,我们的用户一般都会有一个角色。那么在User类里面一般也是以一个实体类来表示。
@Data publicclassUser{ //省略用户属性... //角色信息 privateRolerole; }
我们在查询用户的时候,如果也希望看到它的角色信息,我们会这样来写查询语句:
<selectid="getUserById"resultType="User"> SELECT u.id, u.username, u.password, u.address, u.email, r.idas'role_id', r.nameas'role_name' FROM USERu LEFTJOINuser_rolesurONu.id=ur.user_id LEFTJOINrolerONr.id=ur.role_id whereu.id=#{id} </select>
如上,就要查询单个用户以及用户的角色信息。不过在这里,我们不能用resultType=User
来返回。
毕竟,User类中只有一个Role对象,并没有role_id和role_name字段属性。
所以,我们要使用association来关联它们。
<resultMapid="userMap"type="User"> <idproperty="id"column="id"></id> <resultproperty="username"column="username"></result> <resultproperty="password"column="password"></result> <resultproperty="address"column="address"></result> <resultproperty="email"column="email"></result> <associationproperty="role"javaType="Role"> <idproperty="id"column="role_id"></id> <resultproperty="name"column="role_name"></result> </association> </resultMap>
最后我们就可以将角色信息一块显示出来:
{ "id":"1001", "username":"后羿", "password":"123456", "address":"北京市海淀区", "email":"510273027@qq.com", "role":{ "id":"3", "name":"射手" } }
事实上,如果你确定关联信息是一对一的情况,有个更简便的方法可以替代association,我们在本文的第五部分-自动填充关联对象再看它是怎么实现的。
四、集合
1、集合的嵌套结果映射
上面我们看到一个用户后羿,它的角色是射手;但大部分时候,我们每个人都不可能只拥有一种角色。所以,我们需要将User类中的角色属性的类型改成List。
@Data publicclassUser{ //省略用户属性... //角色信息 privateList<Role>roles; }
现在就变成了一个用户对应多个角色,所以就不是简单的association。
因为association处理的是有一个类型的关联;而我们这里是有多个类型的关联,所以就需要用到collection属性。
我们整体的resultMap会变成下面这样:
<resultMapid="userMap"type="User"> <idproperty="id"column="id"></id> <resultproperty="username"column="username"></result> <resultproperty="password"column="password"></result> <resultproperty="address"column="address"></result> <resultproperty="email"column="email"></result> <collectionproperty="roles"ofType="Role"> <idproperty="id"column="role_id"></id> <resultproperty="name"column="role_name"></result> </collection> </resultMap>
这样的话,即便你有多个角色也可以被正确显示:
{ "id":"1003", "username":"貂蝉", "password":"123456", "address":"北京市东城区", "email":"510273027@qq.com", "roles":[ { "id":"1", "name":"中单" }, { "id":"2", "name":"打野" } ] }
2、集合的嵌套 Select 查询
在大部分业务系统中,我们都会有一个菜单的表,比如像下面这样,一张Menu表:
id | name | url | parent_id |
---|---|---|---|
1 | 系统管理 | 0 | |
1001 | 用户管理 | /user | 1 |
1002 | 角色管理 | /role | 1 |
1003 | 单位管理 | /employer | 1 |
2 | 平台监控 | 0 | |
2001 | 系统监控 | /system/monitor | 2 |
2002 | 数据监控 | /data/monitor | 2 |
这里我们给菜单分为两级。我们给前端返回菜单的时候,也是需要分级的,不可能将这7条数据平级展示。那么,在这里我们的Menu实体类如下:
@Data publicclassMenu{ privateStringid; privateStringname; privateStringurl; privateStringparent_id; privateList<Menu>childMenu; }
一级菜单,包含一个二级菜单的列表,这里用childMenu来表示。
SQL语句中,如果没有parent_id字段属性,我们就先查所有的一级菜单:
<selectid="getMenus"resultMap="menusMap"> SELECT m.id, m.name, m.url, m.parent_id FROM m_menum where1=1 <choose> <whentest="parent_id!=null"> andm.parent_id=#{parent_id} </when> <otherwise> andm.parent_id='0' </otherwise> </choose> </select>
这个查询语句,在不传输任何参数的情况下,我们会得到两条一级菜单的数据。
那么在只调用此方法一次的情况下,怎么把所有的菜单信息查询出来,并按层级展示呢?
我们来看menusMap的定义:
<resultMapid="menusMap"type="Menu"> <idproperty="id"column="id"></id> <resultproperty="name"column="name"></result> <resultproperty="url"column="url"></result> <resultproperty="m_desc"column="m_desc"></result> <resultproperty="parent_id"column="parent_id"></result> <collectionproperty="childMenu"ofType="Menu"select="getMenus"column="{parent_id=id}"></collection> </resultMap>
重点来看collection元素:
property="childMenu"
对应的是菜单中的子级菜单列表;
ofType="Menu"
对应返回数据的类型;
select="getMenus"
指定了SELECT语句的id;
column="{parent_id=id}"
则是参数的表达式。
这个collection整体的含义可以这样理解:
通过getMenus这个SELECT语句来获取一级菜单中的childMenu属性结果;在上面的SELECT语句中,需要传递一个parent_id参数;这个参数的值就是一级菜单中的id。
通过这种方式,我们就可以得到已分级的所有菜单信息。
[ { "id":"1", "name":"系统管理", "parent_id":"0", "childMenu":[ { "id":"1001", "name":"用户管理", "url":"/user", "parent_id":"1" }, { "id":"1002", "name":"角色管理", "url":"/role", "parent_id":"1" }, { "id":"1003", "name":"单位管理", "url":"/employer", "parent_id":"1" } ] }, { "id":"2", "name":"平台监控", "parent_id":"0", "childMenu":[ { "id":"2001", "name":"系统监控", "url":"/system/monitor", "parent_id":"2" }, { "id":"2002", "name":"数据监控", "url":"/data/monitor", "parent_id":"2" } ] } ]
五、自动填充关联对象
我们知道,在Mybatis解析返回值的时候。
第一步是获取返回值类型,拿到Class对象,然后获取构造器,设置可访问并返回实例,然后又把它包装成MetaObject对象。
从数据库rs中拿到结果之后,会调用MetaObject.setValue(String name, Object value)
来填充对象。
在这过程中,有趣的是,它会以.来分隔这个name属性。
如果name属性中包含.符号,就找到.符号之前的属性名称,把它当做一个实体对象来处理。
可能笔者在这里描述的不够直观,我们还是来看例子。
在本文第三部分中,我们有一个用户对应一个角色的例子。
其中,User类定义如下:
@Data publicclassUser{ //省略用户属性... //角色信息 privateRolerole; }
在这里,我们无需定义resultMap,直接返回resultType=User即可。不过需要把role信息的别名修改一下,重点是.符号
<selectid="getUserList"resultType="User"> SELECT u.id, u.username, u.password, u.address, u.email, r.idas'role.id', r.nameas'role.name' FROM USERu LEFTJOINuser_rolesurONu.id=ur.user_id LEFTJOINrolerONr.id=ur.role_id </select>
这样,在Mybatis解析到role.id属性的时候,以.符号分隔之后发现,role别名对应的是Role对象,则会先初始化Role对象,并将值赋予id属性。
相关代码如图:
看完上述内容,你们掌握Mybatis中resultMap如何使用的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注恰卡编程网行业资讯频道,感谢各位的阅读!
推荐阅读
-
Mybatis中怎么利用useGeneratedKeys获取自增主键
Mybatis中怎么利用useGeneratedKeys获取自增主键,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以...
-
Mybatis中怎么实现SQL防注入
这篇文章给大家介绍Mybatis中怎么实现SQL防注入,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。什么是S...
-
mybatis中怎么利用注解对对象进行批量更改
这期内容当中小编将会给大家带来有关mybatis中怎么利用注解对对象进行批量更改,文章内容丰富且以专业的角度为大家分析和叙述,阅读...
-
mybatis(如何判断list集合是否包含指定数据)
mybatis,如何判断list集合是否包含指定数据需求1、在mybatis脚本中想要判断list中是否含有某个字符串。2、动...
-
Mybatis如何自动生成数据库表的实体类
-
Mybatis中多个对象包含同一个对象的处理操作
-
mybatis配置对象包含对象以及List的方式
mybatis配置对象包含对象以及List的方式mybatis配置对象包含对象及List这里隐藏getset方法publ...
-
如何使用mybatis查询语句
本篇文章给大家分享的是有关如何使用mybatis查询语句,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收...
-
解决mybatis中的mapper命名问题
mybatismapper命名问题mapper文件中id命名最好首字母小写,避免让mybatis认为是一个类<...
-
SelectKey怎么在Mybatis中应用