时用得最多的工具,因此熟 练掌握它十分必要。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语句前,我们需要定义参数,它可以是一个简单的参数类型,例如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 id= "countFirstName ” parameterType= " string ” resultType= " int ” >
select count (*) as total from t_user where name like concat (# { firstName),'%;)
</select>
我们在接口
public int countFirstNcme(String firstName);
这样就可以使用MyBatis调用SQL 了,十分简单。下面对操作步骤进行归纳概括。
- id标出了这条SQL。
- parameterType定义参数类型。
- resultType定义返回值类型。
当然这个例子只是为了用来入门,我们将来遇到的问题远比这个复杂得多。
3、自动映射
会提 供自动映射的功能,只要返回的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);
提供的别名功能使得查询结果 和JavaBean的属性一一对应起来,然后MyBatis提供的自动映射的功能使得我们无需过多 的提供配置信息,大大减少了我们的工作量。
属性值来设置其策略。它包 含3个值。
- NONE,取消自动映射。
- PARTIAL,只会自动映射,没有定义嵌套结果集映射的结果集。
- FULL,会自动映射任意复杂的结果集(无论是否嵌套)。
默认值为PARTIAL0所以在默认的情况下,它可以做到当前对象的映射,使用FULL 是嵌套映射,在性能上会下降。
釆用驼峰式命名 方法,那么你也可以设置mapUnderscoreToCamelCase true,这样就可以实现从数据库到
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所示。
就会知道 #{roleName}代表rolename参数,参数的可读性大大提高了。但是这会引起另一个麻烦,一 条SQL拥有10个参数的查询,如果我们都使用@Param方式,那么参数将十分复杂,可读 性依旧不高,不过MyBatis为我们提供了
(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属性定义
- 这样select语句就不再需要使用自动映射的规则,直接用resultMap属性指定
通过自动映射来完 成,这样配置的工作量就会大大减少,未来随着进一步的学习深入,我们还会讨论resultMap 的高级应用。
三、insert 元素
1、概述
insert元素,相对于select元素而言要简单许多。MyBatis会在执行插入之后返回一个 整数,以表示你进行操作后插入的记录数。insert元素配置详解,如表4.4所示。
虽然元素也不少,但是我们实际操作中常用的元素只有几个,并不是很难掌握,让我们 做一个插入角色的操作。首先需要在映射器中定义我们的插入语句,如代码清单4.10所示。
2、主键回填和自定义
现实中有许多我们需要处理的问题,例如,主键自增字段;MySQL里面的主键需要根据一些特殊的规则去生成,在插入后我们往往需要获得这个主键,以便于未来的操作,而MyBatis提供了实现的方法。
首先我们可以使用keyProperty属性指定哪个是主键字段,同时使用useGeneratedKeys 属性告诉MyBatis这个主键是否使用数据库内置策略生成。
表就是指定了
那么我们可以在XML中进行如代码清单4.11所示的配置。
会用数据库的设置进行处理。 这样做的好处是在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元素
执行完
文件,如代码 清单4-14所示。
插入一张表的记录或者 根据主键删除记录,对于参数传递可以参考select元素传递参数的例子。插入和删除执行 完成MyBatis会返回一个整数显示更新或删除了几条记录。