MyBatis映射器详解——select元素

在映射器中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会自动识别它,据此进行分页。

你可能感兴趣的:(java,mybatis)