MyBatis系列四 映射器(上)
映射器是MyBatis最强大的工具,也是我们使用MyBatis时用得最多的工具,因此熟 练掌握它十分必要。MyBatis是针对映射器构造的SQL构建的轻量级框架,并且通过配置 生成对应的JavaBean返回给调用者,而这些配置主要便是映射器,在MyBatis中你可以根 据情况定义动态SQL来满足不同场景的需要,它比其他框架灵活得多。MyBatis还支持自 动绑定JavaBean,我们只要让SQL返回的字段名和JavaBean的属性名保持一致(或者釆 用驼峰式命名),便可以省掉这些繁琐的映射配置。
一、映射器的主要元素
首先让我们明确在映射器中我们可以定义哪些元素,它们的作用分别是什么,如表4.1 所示。
表4・1映射器的配置
元素名称 |
描 述 |
备 注 |
select |
査询语句,最常用、最复杂的元素之一 |
可以自定义参数,返回结果集等 |
insert |
插入语句 |
执行后返回一个整数,代表插入的条数 |
update |
更新语句 |
执行后返回一个整数,代表更新的条数 |
delete |
删除语句 |
执行后返回一个整数,代表删除的条数 |
parameterMap |
定义参数映射关系 |
即将被删除的兀素,不建议大家使用 |
sql |
允许定义一部分的SQL,然后在各个地方引用 它 |
例如,一张表列名,我们可以一次定义,在 多个SQL语句中使用 |
元素名称 |
描 述 |
备 注 |
resultMap |
用来描述从数据库结果集中来加载对象,它是 最复杂、最强大的元素 |
它将提供映射规则 |
cache |
给定命名空间的缓存配置 |
— |
cache-ref |
其他命名空间缓存配置的引用 |
— |
接下来我们将详细讨论映射器中主要元素的用法。
二、select 元素
1、概述
毫无疑问,select元素是我们最常用也是功能最强大的SQL语言。select元素帮助我们 从数据库中读出数据,组装数据给业务人员。执行select语句前,我们需要定义参数,它可以是一个简单的参数类型,例如int、float. String,也可以是一个复杂的参数类型,例如JavaBean、Map等,这些都是MyBatis接受的参数类型。执行SQL后,MyBatis也提供了强大的映射规则,甚至是自动映射来帮助我们把返回的结果集绑定到JavaBean中。select元素的配置如表4.2所示。
表4-2 select元素的配置
元 素 |
说 明 |
备 注 |
id |
它和Mapper的命名空间组合起来是唯一的,提供给MyBatis调 用 |
如过命名空间和id组合起 来不唯一,MyBatis将抛出 • 异常 |
parameterType |
你可以给出类的全命名,也可以给出类的别名,但使用别名必 须是MyBatis内部定义或者自定义的 |
我们可以选择JavaBean > Map等复杂的参数类型传递 给SQL |
parameterMap |
即将废弃的元素,我们不再讨论它 |
. |
resultType |
定义类的全路径,在允许自动匹配的情况下,结果集将通过 JavaBean的规范映射; 或定义为int、double> float等参数; 也可以使用别名,但是要符合别名规范,不能和resultMap同时 使用 |
它是我们常用的参数之 一,比如我们统计总条数就 可以把它的值设置为int |
元 素 |
说 明 |
备 注 |
|
resultMap |
它是映射集的引用,将执行强大的映射功能,我们可以使用 resultType或者resultMap其中的一个,resultMap可以给予我们自 定义映射规则的机会 |
它是MyBatis最复杂的元 素,可以配置映射规则、级 联、typeHandler 等 |
|
flushCache |
它的作用是在调用SQL后,是否要求MyBatis清空之前査询的 本地缓存和二级缓存 |
取4 默认勺 |
莹为布尔值,true/falseo 莹为false |
useCache |
启动二级缓存的开关,是否要求MyBatis将此次结果缓存 |
取值为布尔值,true/falseo 默认值为true |
|
timeout |
设置超时参数,等超时的时候将抛出异常,单位为秒 |
默认值是数据库厂商提供 的JDBC驱动所设置的秒数 |
|
fetchSize |
获取记录的总条数设定 |
默认值是数据库厂商提供 的JDBC驱动所设置的条数 |
|
statementType |
告诉MyBatis使用哪个JDBC的Statement工作,取值为 STATEMENT(Statement)、PREPARED ( PreparedStatement )、 CallableStatement |
默认值为PREPARED |
|
resultSetType |
这是对JDBC的resultSet接口而言,它的值包括FORWARD, ONLY (游标允许向前访问)、SCROLL_SENSITIVE (双向滚动, 但不及时更新,就是如果数据库里的数据修改过,并不在resultSet 中反应出来)、SCROLL_INSENSITIVE (双向滚动,并及时跟踪 数据库的更新,以便更改resultSet中的数据) |
默认值是数据库厂商提供 的JDBC驱动所设置的 |
|
databaseld |
它的使用请参考第3章的databaseldProvider数据库厂商标识这 部分内容 |
提供多种数据库的支持 |
|
resultOrdered |
这个设置仅适用于嵌套结果集select语句。如果为true,就是假 设包含了嵌套结果集或者是分组了。当返回一个主结果行的时候, 就不能对前面结果集的引用。这就确保了在获取嵌套的结果集的 时候不至于导致内存不够用 |
取6 默认6 |
专为布尔值,true/falseo 皇为false |
resultSets |
适合于多个结果集的情况,它将列出执行SQL后每个结果集的 名称,每个名称之间用逗号分隔 |
很少使用 |
2、简易数据类型的例子
例如,我们需要统计一个姓氏的用户数量。我们应该把姓氏作为参数传递,而将结果设置为整形返回给调用者,如代码清单4.1所示。
代码清单4・1 : select的简单例子
<select id= "countFirstName ” parameterType= " string ” resultType= " int ” >
select count (*) as total from t_user where name like concat (# { firstName),'%;) </select>
我们在接口 UserDao中定义方法。
public int countFirstNcme(String firstName);
这样就可以使用MyBatis调用SQL 了,十分简单。下面对操作步骤进行归纳概括。
- id标出了这条SQL。
- parameterType定义参数类型。
- resultType定义返回值类型。
当然这个例子只是为了用来入门,我们将来遇到的问题远比这个复杂得多。
3、自动映射
有这样的一个参数autoMappingBehavior,当它不设置为NONE的时候,MyBatis会提 供自动映射的功能,只要返回的SQL列名和JavaBean的属性一致,MyBatis就会帮助我们 回填这些字段而无需任何配置,它可以在很大程度上简化我们的配置工作。在实际的情况 中,大部分的数据库规范都是要求每个单词用下划线分隔,而Java则是用驼峰命名法来命 名,于是使用列的别名就可以使得MyBatis自动映射,或者直接在配置文件中开启驼峰命 名方式。
让我们来看一个例子,体验一下自动映射的好处。我们需要通过角色编号查询一个角色,并将结果集映射到角色的JavaBean ±o我们先给出JavaBean,如代码清单4.2所示。
代码清单4・2: Role.java
package com.learn.chapter4.po; public class Role ( private Long id; private String roleName; private String note; public Long getld() { return id; public void setld(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; } }
而数据库表(T_ROLE)的字段如表4.3所示。
表4.3数据库表T ROLE描述 |
||
字 段 |
•类 型 |
说 明 |
ID |
INT(20) |
角色编号,主键,递增 |
ROLE NAME |
VARCHAR(60) |
角色名称 |
NOTE |
VARCHAR(1024) |
备注 |
让我们编写Mapper的映射语句,如代码清单4-3所示。
代码清单4・3:自动映射
<select parameterType="id" id="getRole" resultType="com.learn• chapter4. pojo.Role" > Select id, role_name as roleNamef note from t_role where id =#{id} </select>
对于RoleDao接口,我们提供一个方法。
public Role getRole(Long id);
这样就可以使用它,角色名称(m2_name)使用SQL提供的别名功能使得查询结果 和JavaBean的属性一一对应起来,然后MyBatis提供的自动映射的功能使得我们无需过多 的提供配置信息,大大减少了我们的工作量。
自动映射可以在settings元素中配置autoMappingBehavior属性值来设置其策略。它包 含3个值。
-
- NONE,取消自动映射。
- PARTIAL,只会自动映射,没有定义嵌套结果集映射的结果集。
- FULL,会自动映射任意复杂的结果集(无论是否嵌套)。
默认值为PARTIAL0所以在默认的情况下,它可以做到当前对象的映射,使用FULL 是嵌套映射,在性能上会下降。
如果你的数据库是规范命名的,即每一个单词都用下划线分隔,POJO釆用驼峰式命名 方法,那么你也可以设置mapUnderscoreToCamelCase true,这样就可以实现从数据库到 POJO的自动映射了。
4、传递多个参数
(1)使用Map传递参数
对于RoleDao接口,我们提供一个方法。
public List<Role> findRoleByMap(Map<String, String〉params);
输入上面的代码就可以使用这个方法了,如代码清单4.5所示。
这个方法虽然简单易用,但是有一个弊端:这样设置的参数使用了 Map,而Map需要 键值对应,由于业务关联性不强,你需要深入到程序中看代码,造成可读性下降。MyBatis 为我们提供了更好的实现方法,它就是注解参数的形式,让我们看看如何实现。
(2)使用注解方式传递参数
我们需要使用 MyBatis 的参数注解@Param(org.apache.ibatis.annotations.Param)来实现 想要的功能。操作方法是,把RoleDao接口修改为下面的形式。
我们把映射器的XML修改为无需定义参数类型,如代码清单4-6所示。
当我们把参数传递给后台的时候,通过@Param提供的名称MyBatis就会知道 #{roleName}代表rolename参数,参数的可读性大大提高了。但是这会引起另一个麻烦,一 条SQL拥有10个参数的查询,如果我们都使用@Param方式,那么参数将十分复杂,可读 性依旧不高,不过MyBatis为我们提供了 JavaBean定义参数的方式来解决这个问题。
(3)使用JavaBean传递参数
在参数过多的情况下,MyBatis允许组织一个JavaBean,通过简单的setter和getter方法设置参数,这样就可以提高我们的可读性。首先,定义一个RoleParams的JavaBean,如代码清单4.7所示。
代码清单4・7:定义简易的参数JavaBean
package com.learn.chapter4.params; 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; }
我们用JaveBean改写一下传递参数的例子,如代码清单4-8所示。
同样我们在RoleDao接口提供一个方法。
public List<Role> findRoleByParams(RoleParam params);
这就是通过JavaBean的方式传递多个参数的方式。
(4)总结
- 使用Map传递参数。因为Map导致业务可读性的丧失,从而导致后续扩展和维护 的困难,我们应该在实际的应用中果断废弃这样的传递参数的方式。
- 使用@Param注解传递多个参数,这种方式的使用受到参数个数(n)的影响。当n <=5时,它是最佳的传参方式,它比用JavaBean更好,因为它更加直观;当n>5 时,多个参数将给调用带来困难。
- 当参数个数多于5个时,建议使用JavaBean方式。
5、使用resultMap映射结果集
在某些时候,我们需要处理更为复杂的映射, resultMap为我们提供了这样的模式。我们需要在映射器中定义resultMap,这也是我们常见 的场景,如代码清单4-9所示。
解释一下resultMap的配置。
- 定义了一个唯一标识(id)为roleResultMap的resultMap,用type属性去定义它对 应的是哪个JavaBean (也可以使用别名)。
- 通过id元素定义resultResultMap,这个对象代表着使用哪个属性作为其主键。result 元素定义普通列的映射关系,例如,把SQL结果返回的列role no和type属性定义 JavaBean的属性roleNo等做一一对应。
- 这样select语句就不再需要使用自动映射的规则,直接用resultMap属性指定 roleResultMap即可,这样MyBatis就会使用我们的自定义映射规则。
resultMap可没有你所看见的那么简单,它是映射器中最为复杂的元素,它一般用于复杂、级联这些关联的配置。在简单的情况下,我们可以使用resultType通过自动映射来完 成,这样配置的工作量就会大大减少,未来随着进一步的学习深入,我们还会讨论resultMap 的高级应用。
三、insert 元素
1、概述
insert元素,相对于select元素而言要简单许多。MyBatis会在执行插入之后返回一个 整数,以表示你进行操作后插入的记录数。insert元素配置详解,如表4.4所示。
虽然元素也不少,但是我们实际操作中常用的元素只有几个,并不是很难掌握,让我们 做一个插入角色的操作。首先需要在映射器中定义我们的插入语句,如代码清单4.10所示。
2、主键回填和自定义
现实中有许多我们需要处理的问题,例如,主键自增字段;MySQL里面的主键需要根据一些特殊的规则去生成,在插入后我们往往需要获得这个主键,以便于未来的操作,而MyBatis提供了实现的方法。
首先我们可以使用keyProperty属性指定哪个是主键字段,同时使用useGeneratedKeys 属性告诉MyBatis这个主键是否使用数据库内置策略生成。
我们的例子中t role表就是指定了 id列为自增字段。因此我们建立POJO,它能提供setter和getter方法,这样便能够使用MyBatis的主键回填功能了。
那么我们可以在XML中进行如代码清单4.11所示的配置。
这样我们传入的role对象就无需设置id的值,MyBatis会用数据库的设置进行处理。 这样做的好处是在MyBatis插入的时候,它会回填JavaBean的id值。我们进行调试,在插 入后,它自动填充主键,方便以后使用。
让我们测试一下这个功能,如代码清单4.12所示。
代码清单4-12:主键回填测试
sqlSession = SqlSessionFactoryUtil.openSqlSession (); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = new Role(); role.setRoleName("test4"); role.setRoleName("test4Note");
roleMapper.insertRole(role); System.err.printIn(role.getId());
让我们看看调试的结果,如图4.1所示。
实际工作往往不是我们想象的那么简单,需要根据一些特殊的关系设置主键id的值。假设我们取消表t role的id自增的规则,我们的要求是:如果表t_role没有记录,则我们需要设置id=l,否则我们就取最大id加2,来设置新的主键,对于一些特殊要求,MyBatis 也提供了应对方法。
这个时候我们可以使用selectKey元素进行处理,操作方法如代码清单4-13所示。
代码清单4-13:使用自定义主键生成规则
<insert id ="insertRole" parameterType="role" useGeneratedKeys= "true" keyProperty="id"> <selectKey keyProperty="id" resultType="int" order= "BEFORE" > select if(max(id) is null, 1, max(id) + 2) as newld from t_role
</selectKey> insert into t_role(id, role_namez note) values (#(id), #{roleName},#(note)) </insert>
这样我们就能定义自己的规则来生成主键了,MyBatis的灵活性也得以体现。
四、update元素和delete元素
这两个元素比较简单,所以我们放在一起讨论。和insert元素一样,MyBatis执行完 update元素和delete元素后会返回一个整数,标出执行后影响的记录条数。
让我们看看映射文件的配置方法,我们来配置更新和删除角色的XML文件,如代码 清单4-14所示。
我们所面对的大部分场景都和这相似,需要通过一个JavaBean插入一张表的记录或者 根据主键删除记录,对于参数传递可以参考select元素传递参数的例子。插入和删除执行 完成MyBatis会返回一个整数显示更新或删除了几条记录。