Mybatis映射器之select解析
Mybatis映射器之select解析
1. 映射器概述
MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的轻量级的框架,并且通过配置生成对应的JavaBean返回给调用者,并且比普通的方法做的更好。
2. 映射器主要元素
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
cache – 给定命名空间的缓存配置。
cache-ref – 其他命名空间缓存配置的引用。
resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
parameterMap – 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
sql – 可被其他语句引用的可重用语句块。
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句
接下来我们对这些顶级元素进行详细分析。
3. select元素
3.1 概述
select元素使我们最常使用也是功能最强大的SQL语句。select元素帮助我们从数据库中读出数据,组装成需要的数据给业务人员。执行select语句前,我们需要定义参数,参数可以是一个简单的类型,如int、float、String等,也可以是一个复杂的类型,例如JavaBean、Map等,这些都是mybatis接受的参数类型。执行SQL后,mybatis也提供了强大的映射规则,甚至能自动来帮助我们来把数据绑定到JavaBean中。我们首先看下select元素的属性:
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
resultType | 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。 |
resultMap | 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。 |
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。 |
useCache | 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 |
fetchSize | 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。 |
3.2 简单的示例
我们以一个简单的示例来看下select。假如我们要统计同一个班级的学生数量,这时我们应该把班级作为参数传递,而结果返回为学生数量的整型数,如下代码:
<select id="countByClass" resultType="int" parameterType="java.lang.Long" databaseId="mysql">
select count(id) as total from t_user where class_id = #{classId,jdbcType=BIGINT}
</select>
我们还需要在接口中定义这个方法,如下:
public int countByClass(long classId);
这样我们就可以是mysql来调用这条SQL了,具体的步骤如下:
(1)使用id标识出sql
(2)parameterType定义参数类型
(3)resultType定义返回类型
3.3 自动映射
在mybatis中存在一个参数:autoMapperBehavior。当次参数被设置不为空时,mybatis会提供自动映射的功能,只要我们的sql语句返回的列名与对应的JavaBean的属性一致时,mybatis就是帮我们自动的填充JavaBean的字段而无需我们做任何的配置,这样就极大的简化了我们的配置工作。在实际情况中我们命名数据库属性一般都是单词间用下划线分隔的,而java中的属性都是用驼峰命名法来命名,因此这种情况下我们就使用数据库列的别名来让mybatis自动映射,或者在mybatis的配置文件中开启驼峰命名方式。
接下来我们就用一个简单的示例来感受下mybatis的自动映射功能。假如我们要查询某个系统角色Role,并把结果映射成java中Role,我们先定义下JavaBean,如下代码:
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;
}
}
我们数据库中的表结构及数据如下图所示:
接下来我们写RoleMapper.xml文件中的sql,如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yhl.mybatis.mapper.TimeMapper">
<select id="findRoleById" resultType="com.yhl.mybatis.model.Role" parameterType="java.lang.Long" >
select id,role_name as roleName,note from t_role where id = #{roleId,jdbcType=BIGINT}
</select>
</mapper>
编写对应的接口文件及方法,如下:
public interface RoleMapper {
Role findRoleById(Long roleId);
}
这样我们就可以使用它了,角色名称(role_name)使用了sql提供的别名功能,这样查询结果就与JavaBean的属性对应起来了。
自动映射可以在mybatis的配置文件的settings元素中配置autoMapperBehavior的属性值来设置策略,他包含三个值:
(1)NONE,取消自动映射
(2)PARTIAL,只会自动映射,没有定义嵌套结果集映射的结果集
(3)FULL,会自动映射任意复杂的结果集
默认值时PARTIAL,所以在默认情况下,它可以做到当前对象的映射,如果使用FULL的话,在性能上会下降。
3.4 传递多个参数
在上面的例子里我们只是传递了一个参数给映射器,但是在实际情况下我们需要传递多个参数给映射器,接下来我们再通过简单的示例来分析下多个参数的情况。
我们还是用上面的例子,不过这次是使用角色名称和note模糊来查询,需要传递两个参数。
传递多个参数有多种方式,首先我们使用Map的方式,我们先定义下sql语句,如下:
<select id="findRoles" resultType="com.yhl.mybatis.model.Role" parameterType="java.util.Map" >
select id,role_name as roleName,note from t_role
where role_name like concat('%',#{roleName,jdbcType=VARCHAR},'%')
and note like concat('%',#{note,jdbcType=VARCHAR},'%')
</select>
在接口中定义方法:
List<Role> findRoles(Map<String,Object> params);
在传递参数时我们需要定义好Map,如下:
Map<String,Object> params = new HashMap<>();
params.put("roleName","test");
params.put("note","test");
这种方式用起来非常的简单,但是存在一个弊端,因为Map使用的是键值对,跟业务关联性不大,需要我们深入到代码中才能了解。
接下来我们使用注解的方式来传递参数,还是先定义好我们的sql,如下:
<select id="findRoles1" resultType="com.yhl.mybatis.model.Role" >
select id,role_name as roleName,note from t_role
where role_name like concat('%',#{roleName,jdbcType=VARCHAR},'%')
and note like concat('%',#{note,jdbcType=VARCHAR},'%')
</select>
在上面的sql中我们并没有定义参数及类型,这就是注解带来的好处,接下来需要在接口中定义方法:
List<Role> findRoles1(@Param("roleName") String roleName,@Param("note") String note);
如上,当我们把参数传递给后台的时候,通过使用@Param注解提供的名称mybatis就会知道对应的参数,这样参数的可读性大大的提高了,不过这样会引起另一个麻烦,一条sql如果有太多的参数的话,通过这种方式参数会相当复杂,可读性会变很差。mybatis提供了使用javaBean定义参数的方式来解决这种问题。
要使用JavaBean方式传递参数的话我们需要先定义一个参数的bean,如下:
public class RoleParam {
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;
}
}
同样的我们定义下sql,如下:
<select id="findRoles2" resultType="com.yhl.mybatis.model.Role" parameterType="com.yhl.mybatis.model.param.RoleParam">
select id,role_name as roleName,note from t_role
where role_name like concat('%',#{roleName,jdbcType=VARCHAR},'%')
and note like concat('%',#{note,jdbcType=VARCHAR},'%')
</select>
在接口中定义对应的方法:
List<Role> findRoles2(RoleParam param);
这就是使用javaBean传递参数的方式。
接下来我们简单的对比下几种传递参数的方式:
(1)使用Map传递参数,因为使用Map导致业务的可读性降低,这样在后期进行扩展和维护时增加困难,因此在实际开发中我们尽量不要使用这种方式
(2)使用注解@Param方式传递参数,这种情况受到参数个数的限制,当参数个数<=5时,他是最佳的传递方式,当>5时,最好使用JavaBean的方式
(3)当传递参数个数超过5个时,最好使用JavaBean的方式。
3.5 使用resultMap来映射结果集
上面我们介绍了mybatis的自动映射功能,但是在某些时候,我们需要处理更负责的映射,resultMap就为我们提供这样的功能,接下来我们就简单的介绍了resultMap,首先我们需要在XML文件中定义好resultMap,我们还是以之前的例子来分析下,如下所示为定义好的resultMap:
<resultMap id="resultMap" type="com.yhl.mybatis.model.Role">
<id property="id" column="id" javaType="String" jdbcType="BIGINT"/>
<result property="roleName" column="role_name" javaType="String" jdbcType="VARCHAR"/>
<result property="note" column="note" javaType="String" jdbcType="VARCHAR"/>
</resultMap>
然后改造下我们的sql,如下:
<select id="findRoles2" resultMap="resultMap" parameterType="com.yhl.mybatis.model.param.RoleParam">
select id,role_name ,note from t_role
where role_name like concat('%',#{roleName,jdbcType=VARCHAR},'%')
and note like concat('%',#{note,jdbcType=VARCHAR},'%')
</select>
我们详细的解析下resultMap,
(1)定义一个唯一的id来标识resultMap,用type属性来定义它所对应的JavaBean
(2)通过id元素来定义主键,result元素定义普通列的映射关系,如上所示,我们把role_name和note属性分别与JavaBean的属性一一对应。
(3)这样select语句就不再需要使用自动映射规则了,可以直接使用resultMap属性指定定义的resultMap。