在映射器中select元素代表SQL的select语句,用于查询。在SQL中,select语句是用的最多的语句,在MyBatis中select元素也是用的最多的元素,使用的多就意味着强大和复杂。先来看看select的配置:
- id:它和Mapper的命名空间组合在一起是唯一的,供MyBatis调用
- parameterType:可以给出类的全命名,也可以给出别名,但是别名必须是MyBatis内部定义或者自定义的。
- resultType:定义类的全路径,在允许自动匹配的情况下,结果集将通过JavaBean的规范映射;或定义为int、double、float、map等参数。
- resultMap:它是映射集的引用,将执行强大的映射功能。我们可以使用resultType和resultMap其中的一个
- flushCache:它的作用是在调用SQL后,是否要求MyBatis清空之前查询本地缓存和二级缓存,取值为布尔型,默认值为false
- useCache:启用二级缓存的开关,是否要求MyBatis将此结果缓存,取值为布尔型,默认值为true
1、简单的select元素应用
我们先来看一个简单的例子:统计用户表中同一个姓氏的用户数量。代码如下:
<select id="countUserByFirstName" parameterType="string" resultType="int">
select count(*) total from user
where user_name like concat(#{firstName},'%')
<select>
- parameterType表示这条SQL接受的参数类型,可以是MyBatis系统定义或者是自定义的别名,比如int,string,float等,也可以是类的全限定名,比如com.ssm.pojo.User。
- resultType表示这条SQL返回的结果类型,与parameterType一样,可以是系统定义或者自定义的别名,也可以是类的全限定名。
- #{firstName}是被传递出去的参数。
只是这条SQL还不够,我们还需要一个接口方法程序才能运行起来,比如SQL可以这样定义接口方法:
public Integer countUserByFirstName(String firstName);
2、自动映射和驼峰映射
MyBatis提供了自动映射功能,使用自动映射的好处在于能有效减少大量的映射配置,从而减少工作量。我们将以角色表(字段有 id,roleName,note)为例。
在setting元素中有两个可以配置的选项autoMappingBehavior和mapUnderscoreToCamelCase,他们是控制自动映射和驼峰映射的开关。
配置自动映射的autoMappingBehavior选项的取值范围是:
- NONE:不进行自动映射。
- PARTIAL:默认值,只对没有嵌套结果集进行自动映射。
- FULL:对所有的结果集进行自动映射,包括嵌套结果集。
在默认情况下,我们一般使用默认的PARTIAL。为了实现自动映射,首先要给出一个POJO——Relo,代码如下:
package pojo;
public class Role {
private Long id;
private String roleName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
这是一个十分简单的POJO。如果编写的SQL列名和属性名保持一致,那么他就会形成自动映射,比如通过id获取角色的信息。
<select id="getRole" parameterType="long" resultType="role">
select id,role_name as roleName,note from t_role where id=#{id}
</select>
原来的列名role_name被别名roleName代替了,这样就和POJO上的属性名称保持一致了。此时MyBatis就会将这个结果集映射到POJO的属性roleName上,自动完成映射,就无须在进行任何配置,明显减少了工作量。
3、传递多个参数
在现实需求中可以有多个参数,比如订单可以有订单号查询,也可以根据订单名称、日期等参数进行查询,为此要研究一下多个参数的场景。假设要通过角色名称(role_name)和备注(note)对角色进行模糊查询,这样就有2个参数了。
(1)、使用map接口传递参数
在MyBatis中运行map接口通过键值对传递多个参数,把接口方法定义为:
public List<Role> findRolesByMap(Map<String,Object>parameterMap);
此时,传递给映射器的是一个map对象,使用它在SQL中设置对应的参数,代码如下:
<select id="findRolesByMap" parameterType="map" resultType="role">
select id,role_name as roleName,note from role
where role_name like concat('%',#{roleName},'%')
and note like concat('%',#{note},'%')
</select>
注意,参数roleName和note,要求的也是map的键,也就是需要如下代码传递参数:
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Map<String,Object> parameterMap = new HashMap<String,Object>();
parameterMap.put("roleName","1");
parameterMap.put("note","1");
List<Role> roles=roleMapper.findRolesByMap(parameterMap);
在SQL中的参数标识将会被这里设置的参数所取代,这样就能运行了。严格来说,map几乎适用于所有场景,但是我们用的不多,原因有两个:1、map是一个键值对应的集合,使用者要通过阅读它的键,才能明白其作用;2、map不能限定其传递参数的数值类型,因此业务能力不强,可读性差。
(2)、使用注解传递参数
MyBatis可以通过注解@param去定义映射器的参数名称,使用它可以得到更好的可读性,把接口方法定义为:
public List<Role> findRolesByAnnotation(@Param("roleName")String rolename,
@Param("note")String note);
此时代码的可读性大大提升了,使用者能明确参数roleName是角色名称,而note是备注,一目了然,这时候的映射文件代码如下:
<select id="findRolesByAnnotation" resultType="role">
select id,role_name as roleName,note from role
where role_name like concat('%',#{roleName},'%')
and note like concat('%',#{note},'%')
</select>
(3)、通过JavaBean传递多个参数
先定义一个参数的POJO——RoleParams,代码如下:
public class RoleParams {
private String roleName;
private String note;
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
此时把接口方法定义为:
public List<Role> findRolesByBean(@Param(RoleParams roleParam);
JavaBean的属性roleName代表角色名称,而note代表备注,然后修改映射文件:
<select id="findRolesByBean" parameterType="pojo.RoleParams" resultType="role">
select id,role_name as roleName,note from role
where role_name like concat('%',#{roleName},'%')
and note like concat('%',#{note},'%')
</select>
引入JavaBean定义的属性作为参数,然后查询。
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
RoleParams roleParams = new RoleParams();
roleParams.setRoleName("1");
roleParams.setNote("1");
List<Role> roles=roleMapper.findRolesByBean(roleParams);
(4)、混合使用
在某些情况下需要混合使用几种方法传递参数。举个例子,查询一个角色,可以通过角色名称和备注进行查询,与此同时还需要支持分页,而分页的POJO实现代码如下:
public class PageParams{
private int start;
private int limit;
}
这个时候接口设计就得如下所示了:
public List<Role> findRolesByMix(@Param("params") RoleParams roleParams,
@Param("page") PageParams pageParams);
这样设置之后,映射文件也要做出相应的改变:
<select id="findRolesByMix" resultType="role">
select id,role_name as roleName,note from role
where role_name like concat('%',#{params.roleName},'%')
and note like concat('%',#{params.note},'%')
limit #{page.start},#{page.limit}
</select>
总结
- 使用map传递参数导致了业务可读性的丧失,导致后续扩展和维护的困难,在实际的应用中要果断废弃。
- 使用@Param注解传递多个参数,收到参数个数(n)的影响。当n<=5时,这是最佳的传值方式;当n>5时,多个参数将给带来困难,此时不推荐使用它。
- 当参数大于5时,建议使用JavaBean方式。
- 对于使用混合参数,要明确参数的合理性。
4、使用resultMap映射结果集
自动映射和驼峰映射规则比较简单,无法定义多的属性,比如typeHandler、级联等。为了支持复杂的映射,select元素提供了resultMap属性。先定义resultMap属性,代码如下:
<mapper namespace="mapper.RoleMapper">
<resultMap id="rolemap" type="role>
<id property="id" column="id"/>
<result property="roleName" column="role_name" />
<result property="note" column="note" />
</resultMap>
<select id="getRoleUseResultMap" parameterType="long" resultType="roleMap">
select id,role_name,note from role
where id=#{id}
</select>
</mapper>
解释一下这段代码的含义:
- resultMap元素定义了一个roleMap,它的属性id代表它的标识,type代表使用哪个类作为其映射的类,可以是别名或者全限定名。
- 他的子元素id代表resulMap的主键,而result代表其属性,id和result元素的属性property代表POJO的属性名称,而column代表SQL的列名。把POJO的属性和SQL的列名做对应。
- 在select元素中的属性resultMap制定了采用哪个resultMap作为其映射规则。
5、分页参数RowBounds
MyBatis不仅支持分页,它还内置了一个专门处理分页的类——RowBounds。
RowBounds源码如下所示:
package other;
public class RowBounds {
public static final int NO_ROW_OFFSET=0;
public static final int NO_ROW_LIMIT=Integer.MAX_VALUE;
public static final RowBounds DEFAULT=new RowBounds();
private int offset;
private int limit;
public RowBounds(){
this.offset=NO_ROW_OFFSET;
this.limit=NO_ROW_LIMIT;
}
public RowBounds(int offset,int limit){
this.offset=offset;
this.limit=limit;
}
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
}
offset属性是偏移量,即从第几行开始读取记录。limit是限制条数,从源码可知,默认值为0和java最大整数(2147483647),使用它十分的简单,只要给接口增加一个RowBounds参数即可。
public List<Role> findRolesByRowBounds(@Param("roleName")String rolename,
@Param("note")String note,
RowBounds rowBounds);
对于SQL而言,映射内容如下:
<select id="findRolesByRowBounds" resultType="role">
select id,role_name as roleName,note from role
where role_name like concat('%',#{roleName},'%')
and note like concat('%',#{note},'%')
</select>
注意,映射文件中没有任何关于RowBounds参数的信息,它是MyBatis的一个附加参数,MyBatis会自动识别它,据此进行分页。