MyBatis 初级篇
MyBatis 实验环境搭建
前言
MyBatis 是一个理论少,实践性强的框架;它没有太多的概念,最好的学习方式就是实践。本小节,我们将一起搭建 MyBatis 的实践环境,方便后续章节的学习。
新建项目
考虑到工程的维护性,我们选择 IDE 来新建一个 Maven 项目来使用 MyBatis。当然如果你更倾向了 Gradle,那么没有关系,你只需要更改添加依赖的方式即可。
在 IDE 上,你可以选择 Eclipse 或者 IDEA,当然我们更推荐你使用 IDEA,因为它的社区版已经足够我们学习 MyBatis 了,而且它也是免费的,本小节我们以 IDEA 作为默认的开发环境。
打开 IDEA,选择 New Project,点击左侧的Maven项,然后 Next 新建项目,如下图:
进入下一页后,输入对应的 GroupId 和 ArtifactId,如下图,你也可以选择自己心仪的 id,但是我们推荐你跟我们保持一致,这样在后面的学习中,你的配置和代码才能跟我们完全一致。
填完以后,点击 Next 直到出现 Finish,点击完成即可。
添加依赖
项目新建后,在项目根目录下找到 pom.xml文件,并向其中添加如下配置。
1  | <dependencies>  | 
添加的依赖比较多,虽然在相应的地方我们也打上了注释,不过我们依然得说明一下。dependency 是 Maven 管理依赖的方式,我们分别添加了 mybatis、mysql-connector-java 和 logback-classic。
其中 MyBatis 作为我们的主角,它的依赖是必不可少的;由于实操需要数据库环境,我们也添加上了 MySQL 驱动依赖;为了更好的查看信息,我们也添加了 logback 日志框架。
另外,由于 Maven 打包默认不会打包 src/main/java文件夹下的资源文件,但实际的环境中,我们可能需要在该文件夹下存放资源文件,如.xml,所以我们也必须更改这个配置。
添加依赖后,IDE 会提供你是否导入这些依赖,请你点击确认,并且等待一会儿,待依赖导入完成我们就可以进入下一步了。
数据准备
项目搭建好后,我们还需要一定的数据支持。首先,请在你可用的数据库环境中新建一个名为mybatisdemo的数据库,当然你也可以使用其它的名称,但还是希望你能与我们保持一致,新建数据库成功后,接着运行以下 SQL 脚本。
1  | DROP TABLE IF EXISTS user;  | 
结果如下:
1  | +----+----------+-----+-------+  | 
小结
本小节是一个纯实操小节,我们没有介绍任何概念,而是带你一起搭建了学习 MyBatis 需要的环境和数据,后续的所有小节都将直接依赖于本小节。
MyBatis 简单使用
前言
在上面中,我们搭建了 MyBatis 实验环境。本小节,我们将一起学习如何使用 MyBatis,虽然在实际的开发中,你几乎不会按照本小节所介绍的方式去使用 MyBatis,但是这对你熟悉 MyBatis 整体结构有着重要作用,同时这也是面试的重点。
编程式使用
MyBatis 官方文档中并未详细的介绍如何编程式使用 MyBatis,绝大多数情况下,我们都是通过 配置文件来拿到配置然后开启会话的。这样的方式固然很方便,但是却屏蔽了太多的细节,因此我们想从点到面,层层递进给你介绍 MyBatis 的基础用法。
接下来,我们一起来写一个简单的 demo 来使用一下 MyBatis。
启动 MyBatis
在 mybatis-primer 项目中,有一个默认的包com.ahao.mybatis,在该包下,我们新建一个包名为pattern,并在其中新建一个名为StartNoXml.java的类,并向该文件中填充如下代码:
1  | package com.ahao.mybatis.pattern;  | 
即使你不熟悉 MyBatis,也没有必要被这段代码给吓到,因为在实际的开发中,你几乎没有机会去写这段代码。但是我们仍需要介绍这段代码,它可能是你面试的重点。
书写完毕后,请在 PooledDataSource 类构造函数中更改数据用户名、密码和 url 配置以满足你所使用的数据库环境。运行一下这段代码,如果一切顺利,在控制台中你会看到以下输出内容(只截取了部分内容):

使用流程
在代码中,我们添加了一定量的注释说明了流程,接下来我们来总结一下。
对于 MyBatis 的基础使用可大致分为以下3步:
- 得到 MyBatis 配置信息,即代码中的
Configuration类。Configuration 负责 MyBatis 架构中的配置部分,例如:dataSource数据源信息都会交给 Configuration 去管理;这一步其实是比较繁杂的,Environment 是 Configuration 中的一部分,而 PooledDataSource 和 JdbcTransactionFactory 又是 Environment 中的一部分,它们是属于层层递进的关系。其中 JdbcTransactionFactory 表示事务工厂,当 MyBatis 需要新建事务的时候,会通过它来新建;PooledDataSource 表示数据源,通过其构造参数,我们传入了数据库 url,数据库用户和密码等配置;Configuration 可以有多个 Environment,因此每个 Environment 都必须有唯一的 id,即代码中的 development,将这些配置搭配组合后就是一个可用的 Configuration。 - 通过 Configuration 来创建 
SqlSessionFactory。MyBatis 是通过会话的方式来执行 SQL 的,因为我们必须拥有一个会话创建器,即会话工厂。 - 新建
SqlSession来执行 SQL。有了 SqlSessionFactory 后,我们就可以方便地新建会话,并通过会话来执行 SQL 了。 
而PreparedStatement及以下的内容,其实并不属于 MyBatis,它们是 JDBC 提供的,在实际的 MyBatis 开发中,你也不会这样去执行 SQL,在这里我们只是为了展示 MyBatis 和 JDBC 的关系。
可以看到,编程式使用 MyBatis 其实是比较复杂,你需要十分熟悉 MyBatis 的 API,而且这种硬编码的方式是比较笨重的,所以绝大多数资料都推荐配置的方式使用 MyBatis。
配置式使用
接下来,我们一起来看一下如何通过配置来使用 MyBatis。
配置文件
首先,我们在resources目录下新建mybatis-config.xml配置文件,并在其中添加上如下配置:
1  | 
  | 
有了上面编程式 API 的使用经验,那么你一定可以轻松的看懂配置项,configuration 标签对应 Configuration 类,environment 标签对应 Environment 类,transactionManager标签和dataSource标签分别对应 JdbcTransactionFactory 和 PooledDataSource 类。
有了配置文件后,我们无需一个挨着一个的新建类,而是在配置文件中指定即可,如driver的值指定为com.mysql.cj.jdbc.Driver。当后续需要修改的时候,也不需要去代码中找,而是直接在配置文件中修改即可。
TIPS: 注意, 请在你自己的配置文件中修改数据库配置,以满足你自己的数据库环境。
启动 MyBatis
同样地,我们在 patter 包下新建另一个类,名为StartWithXml.java,并填充以下代码:
1  | package com.ahao.mybatis.pattern;  | 
运行代码,你会看到跟上面一样的结果。
使用流程
配置式使用 MyBatis,也可分为3步:
- 读取配置文件,即
mybatis-config.xml。 - 通过配置文件来创建 
SqlSessionFactory。 - 新建
SqlSession来执行 SQL。 
与编程式相比,配置式更为简洁,更易维护,所以使用广泛,几乎所有资料都推荐这种方式来使用 MyBatis。但是编程式并非没有意义,它可以帮助你梳理 MyBatis 结构,有了编程式的基础,你才能更加容易地看懂配置文件中的内容,在后续的学习中,我们都将默认地使用配置式。
小结
- 在实际的开发中,你都少有机会去按照本小节的方式去使用 MyBatis,但是这对你
深入理解MyBatis结构有重要作用。 - 编程式使用的 API 较多,我们没有必要去死记硬背,熟练掌握其使用流程,能在需要的时候查阅即可。
 
MyBatis mapper
前言
本小节,我们将一起学习 MyBatis mapper。
在上一节中我们以 JDBC 的方式使用了 MyBatis,但在实际应用中是不会选择这种方式来执行 SQL 的,MyBatis提供了 mapper 这种优雅且易维护的方式来帮助我们更好地去使用 SQL。
定义
mapper 是 Java 方法和 SQL 语句之间的
桥梁。
Java 接口方法与 SQL 语句以及 mapper 之间的关系如下图所示:

新建 mapper
mapper 只是一个抽象的概念,它其实就是 Java 里面的一个接口类,我们不需要实现这个接口类,MyBatis 会通过动态代理自动帮我们执行接口方法所对应的 SQL 语句。
接下来,我们以UserMapper为例,来看一看 mapper 究竟是如何定义和组成的。
首先,在 com.ahao.mybatis 包下新建 mapper包,并在 mapper 包下新建接口类UserMapper.java。如下:
1  | package com.ahao.mybatis.mapper;  | 
MyBatis 提供了注解和XML两种方式来连接接口方法和 SQL 语句。
注解方式
我们为 UserMapper 添加一个方法selectUsernameById,该方法的作用为通过用户 id 查询用户名称,如下:
1  | package com.ahao.mybatis.mapper;  | 
selectUsernameById 方法接受 id 参数(用户 id),返回用户名称(String 类型)。
有了方法定义后,我们再通过注解为该方法添加上对应的 SQL 语句:
1  | package com.ahao.mybatis.mapper;  | 
Select注解对应 SQL 的 select 查询,注解中的语句就是相应的 SQL 语句,当然这并非真实的 SQL 语句,具体的差异性我们后续章节再说。
XML 方式
XML 方式是更加强大和易用的一种方式,虽然它没有注解那么方便,但是功能更强、更易维护,是 MyBatis 官方推荐的一种方式。
在 mapper 包中,我们新建另一个文件UserMapper.xml,并添加如下内容:
1  | 
  | 
mapper 标签对应一个 mapper 接口类,这里应该对应 UserMapper,所以在 mapper 标签里面我们还需要加上 namespace 这个属性,它的值为 UserMapper 的类全路径,这样 UserMapper.xml 配置文件就与 UserMapper.java 对应起来了。
提示,namespace 命名空间是每一个 mapper 文件所独有的,它唯一标识着一个 mapper。
注意: 在这里,.xml 配置文件必须与其对应的接口在同一个包内。
二者在目录中的位置如下:
1  | src/main/java/com/ahao/mybatis/mapper  | 
在 UserMapper 接口中,我们再新增一个方法selectUserAgeById,该方法的作用是通过用户 id 查询用户年龄。如下:
1  | package com.ahao.mybatis.mapper;  | 
与之对应的 xml 文件中,我们也需要添加上对应的 SQL 语句。如下:
1  | 
  | 
在 mapper 标签中,我们新增了 select 标签,对应 SQL 中的 select 查询;select 标签中有两个必填属性,第一个是 id ,它对应接口的方法名,即 selectUserAgeById,通过它 MyBatis 才能将二者对应起来,第二个是 resultType,它对应 SQL 语句的返回类型,与接口方法的返回值相同,为 Integer 类型。
好了,注解和 XML 的两种方式的简单使用已经介绍完毕了,这里仍然有一个可以完善的点,我们可以为 UserMapper 类打上一个 Mapper注解,虽然这个注解并不是必须的,但是增强了代码的可读性。如下:
1  | // 省略  | 
使用 mapper
mapper 定义好以后,我们接下来会介绍如何使用它。在上一节中,我们介绍了 MyBatis 配置式的简单使用,那么使用 mapper 其实很简单,只需在配置式使用的基础上增加几处配置和代码就行了。
mapper 配置
首先,我们需要在 mybatis-config.xml 配置文件中添加上对应的 mapper 配置:
1  | 
  | 
注意,mapper 可以有多个,对应的标签项应是 mappers,在 mappers 下面才有一个个的 mapper,如上面的 UserMapper;通过mapper 标签中的 class 属性,我们指定其对应的接口类,class 属性值为 UserMapper 的类全路径。
代码调用
有了配置以后,我们则可以在代码中调用 mapper 方法从而执行 SQL 得到结果了。在 pattern 包下,我们新建一个文件,名为StartWithMapper.java,并向其中添加如下代码:
1  | package com.ahao.mybatis.pattern;  | 
使用流程我们已经写在了代码注释中了,请务必阅读一下。
与上一节的区别在于,我们不再通过 session 得到连接从而执行 SQL 了,而是在 session 中得到配置好的 UserMapper,再通过调用UserMapper 的方法执行 SQL 从而得到结果。
执行这段代码,它会在控制台上打印如下信息(截取了部分重要信息):
1  | 13:13:50.104 [main] DEBUG com.ahao.mybatis.mapper.UserMapper.selectUsernameById - ==> Preparing: SELECT username FROM user WHERE id = ?  | 
总结
从程序输出的信息中可以看出,UserMapper 的调用都成功了, 结合上一小节中 JDBC 的使用,可以清晰地感受到 MyBatis 完美的将 Java 对象与 SQL 语句分离,并通过 mapper 充当二者的桥梁,极大的提升了代码和 SQL 语句的维护性。
不同于原生 JDBC 的使用方式,MyBatis 会自动的通过 resultType 等配置来帮我们实现数据库类型到 Java 类型的转换,帮我们节省了大量的工作,当然 MyBatis 的功能远不止如此,我们将在后续的小节中一一揭晓。
小结
- mapper 是 MyBatis 的核心概念,是 MyBatis 
解耦Java 对象与 SQL 语句的桥梁。 - MyBatis 官方文档中明确
强调注解方式 SQL 无法发挥 MyBatis 的全部功能,但是可以方便地演示一些 demo。 - 如果单独使用 MyBatis,那么 mapper 接口必须和 .xml 配置文件在同一个包中,但是如果使用 spring 等工具就可以不必受此限制。
 
MyBatis select
前言
本小节,我们将一起学习 MyBatis select。
在 MyBatis 中,select 标签对应于 SQL 语句中的 select 查询,我们会在 select 标签中填充 SQL 查询语句,然后在代码中通过对应接口方法来调用。
定义
select 标签用于映射 SQL 中的
查询语句
实例
MyBatis select 可分为xml和注解两种使用方式。
xml 实例
将 select 查询写在 mapper.xml 文件中,比如:
1  | 
  | 
其中名为 selectUserAgeById 的 select 标签(一般以 id 做为名称),接收 Integer 类型的参数(parameterType),并返回 Integer 类型的结果(resultType);再看 select 标签中的查询语句,接收 id 参数,类型为 int,返回 age,类型为 int,二者一一对应。
参数符号
注意,在 select 标签中的 SQL 语句几乎与真实的 SQL 语句一致,但它的参数符号稍有不同:
1  | #{id}  | 
若以 #{}作为参数符号,MyBatis 则会创建一个预处理语句(PreparedStatement),它会被处理成?。如果你不希望使用预处理,那么可以使用${}参数符号,MyBatis 会将其以字符串的形式进行拼接,不过我们推荐你使用 #{},几乎所有人也都这样做。
注解实例
将 SQL 语句写在注解中,如下:
1  | 
  | 
注解中的 select 语句无需再添加 id、parameterType 等属性,只需写上对应的 SQL 语句,MyBatis 会自动根据其修饰的方法来推断出这些参数。
TIPS: 使用注解来书写 MyBatis 相对会方便一些,但是注解无法发挥 MyBatis 动态 SQL 真正的威力,因此大部分人都还是会选择 xml 的方式来书写 SQL,但是对于一些 demo 的展示,注解无疑容易上手一些。在后面的学习中,我们都会介绍到注解的使用,但是在实践中我们默认使用 xml 方式。
select 属性
select 标签支持很多属性来改变查询语句的行为。
我们摘取其中常见且重要的属性,如下表所示:
| 属性 | 描述 | 
|---|---|
| id | 在命名空间中唯一的标识符 | 
| parameterType | 语句的参数类型,默认可选,MyBatis 会自动推断 | 
| resultType | 语句返回值类型,如果返回的是集合,那应该设置为集合包含的类型 | 
| resultMap | 语句返回映射的 id;可以使用 resultType 或 resultMap,但不能同时使用。 | 
| flushCache | 设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认为 false | 
| useCache | 设置为 true 后,本条语句的查询结果被二级缓存缓存起来,默认 select 标签为 true。 | 
| timeout | 设置超时时间 | 
| fetchSize | 设置预期返回的记录数量 | 
| statementType | STATEMENT,PREPARED 或 CALLABLE 中的一个,默认为 PREPARED(预处理) | 
参数
在上面的实例中,我们介绍了#{id}参数,这是参数的最简单情况,对于复杂情况,参数还有其它更多的妙用。
对象参数
有时候,参数可以是一个复杂的对象,如 Java 中的一个 User 类。
1  | <select id="selectUserByAgeAndScore" parameterType="com.ahao.mybatis.model.User"  | 
selectUserByAgeAndScore 查询的参数是一个复杂 Java 对象 User,当 User 作为参数对象时,User 中的属性都可作为查询语句的参数,如 age 和 score。
参数配置
#{}不仅可以传入参数名称,还可以传入参数类型和类型处理器。
比如:
1  | #{age,javaType=int,jdbcType=int,typeHandler=IntegerTypeHandler}  | 
其中 javaType 表示 age 的 Java 类型,jdbcType 表示 age 的数据库类型,typeHandler 表示 age 的类型处理器,关于类型处理器我们将在后面的章节介绍。
对于参数配置,90% 的情况你都只需要传入参数名即可,因为 MyBatis 都会自动推断出配置。
实践
介绍了这么多概念,我们一起来实操巩固一下。
例1. 通过年龄和分数查询用户
请使用 MyBatis 完成在 user 表中通过年龄和分数查询用户的功能。
分析:
按照 MyBatis 的开发模式,先在对应 UserMapper.xml 文件中添加通过年龄和分数查询用户的 select 标签,然后在 UserMapper.java 中增加上对应的方法即可。
步骤:
首先,我们在 com.ahao.mybatis 包下新建 model 包,用于保存数据库对象,并在 model 包下新建 User.java 类:
1  | package com.ahao.mybatis.model;  | 
在 UserMapper.xml 文件中,我们新增 selectUserByAgeAndScore 标签,该 select 标签的作用是:通过年龄和分数查询用户。
1  | <select id="selectUserByAgeAndScore" parameterType="com.ahao.mybatis.model.User"  | 
然后在 UserMapper.java 接口中,我们新增对应的方法,方法接收复杂参数 User:
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 selectUserByAgeAndScore 方法。
1  | package com.ahao.mybatis.pattern;  | 
输出结果为:
1  | User{id=1, username='peter', age=18, score=100}  | 
小结
- MyBatis 虽然提供了 
#{}和${}两种方式来传递参数,但无疑#{}更加实用。 - select 标签属性虽然多,但是大部分情况下都不会针对某一个 select 标签来定制化,多会在全局里进行配置。
 - 互联网的应用场景多数为读多写少,那么 select 肯定是使用
最为广泛的标签了。 
MyBatis resultMap 与 sql
前言
本小节,我们将一起学习 MyBatis resultMap 和 sql。
在前面的小节中,我们了解到 MyBatis 可以自动帮助我们映射数据库数据和 Java 对象,其实这是 MyBatis 在幕后帮我们创建了 resultMap 对象;虽然 MyBatis 可以自动帮助我们做数据映射,但是对于复杂的对象,我们就必须自定义 resultMap 了。
而在书写 SQL 时,势必会有一些 SQL 代码段出现了重复,为了更好的复用它们,MyBatis 提供了 sql 标签。
定义
resultMap 标签用于将数据库数据
映射为 Java 对象;sql 标签则用来定义可重用的 SQL 代码段。
实例
resultMap 实例
xml 实例
在下面这段 select 标签中,SQL 语句返回的是一个复杂对象,即 resultType 上指定的 User。
1  | <select id="selectUserByAgeAndScore" parameterType="com.ahao.mybatis.model.User"  | 
在这种情况下,MyBatis 会自动创建 resultMap 对象进行数据的映射,接下来我们直接定义出 resultMap,避免 MyBatis 推断和映射带来的性能损耗。如下:
1  | <resultMap id="userMap" type="com.ahao.mybatis.model.User">  | 
我们定义了名为 userMap 的 resultMap 且指定其对应的 Java 类型为 User,在标签的内部,我们还需指定字段之间的映射,除 id 这个特殊的字段外,其它字段均使用 result 标签来映射。
其中 property 是 Java 对象中的字段名称,column 是数据表与之对应的字段名称。
resultMap 定义完毕后,我们在 select 标签中通过 resultMap 属性来设置对应的 id。
TIPS: 注意, resultMap 和 resultType 不能共存,只能二选一。
这样,一次简单的 resultMap 使用就完毕了。
注解实例
通过注解,我们也可以指定 Java 模型对象与数据库字段之间的映射关系,如下:
1  | 
  | 
Results 注解与 resultMap 对象,包含多个 Result 注解,每个 Result 注解对应了一个字段映射关系。
提示: Results 注解以及 ResultMap 注解虽然存在,但是很少在实际的开发中使用,只需了解即可。
SQL 实例
我们将目光放到 selectUserByAgeAndScore 语句的内部,在实际的开发中像SELECT * FROM user这样的代码段其实非常常见,会在多个 select 标签中用到它。我们可以将其定义为一个 sql 标签,这样所有的 select 标签都可以快速复用到这段代码。
1  | <sql id="selectUser">  | 
同样的,我们必须为这个代码段定义一个唯一的 id,定义好后,我们就可以在其它标签中使用了:
1  | <select id="selectUserByAgeAndScore" parameterType="com.ahao.mybatis.model.User"  | 
这里,我们必须使用一个 include 标签来将 SQL 标签包含进来,并且 refid 属性必须是该 SQL 标签的 id 值。这样这段代码仍然可以正常工作。
SQL 标签没有对应注解,只能在 xml 中使用。
实践
接下来,我们一起来实操巩固一下。
例1. 查询用户姓名和年龄
请使用 MyBatis 完成在 user 表中查询用户简略信息的功能。
分析:
很多应用都会有查询用户简略信息这样一个需求,比如我们只需获取用户名和年龄,运用本小节的知识我们可以这样实现它。
先在对应 UserMapper.xml 文件中添加查询用户简略信息的 select 标签,然后在 UserMapper.java 中增加上对应的方法即可。
步骤:
首先,我们新建一个用户简略信息的模型 UserShortCut.java:
1  | package com.ahao.mybatis.model;  | 
并在 UserMapper.xml 添加上 resultMap 、sql 和 select 标签:
1  | <!-- 复用的 sql 代码段 -->  | 
接下来,给 UserMapper.java 接口添加上对应的方法,以便在程序中调用。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 selectUserShortcutById 方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
结果如下:
1  | UserShortCut{username='peter', age=18}  | 
小结
- SQL 是一个非常好用的标签,能够减少大量重复的 SQL 代码书写,但是也降低 SQL 的可读性。
 - MyBatis 会自动生成 resultMap,这会为我们节省了大量的时间,如果不是对性能十分严苛,那么 resultType 是够用的。
 
MyBatis insert
前言
本小节,我们将一起学习 MyBatis insert。
在 MyBatis 中,insert 标签对应于 SQL 语句中的 insert 插入;与 select 相比,insert 要简单许多,只有当需要返回主键时,才会麻烦一些,我们将从简单到复杂来依次介绍。
定义
insert 标签用于映射 SQL 中的
插入语句
实例
xml 实例
下面是一个简单的 insert 标签。
1  | <insert id="insertUser" parameterType="com.ahao.mybatis.model.User">  | 
同 select 一样,每一个 insert 标签都必须有一个唯一的 id 和可选的 parameterType。标签里面则是真正的 SQL 语句,该语句共有 4 个参数,分别对应 User 类的四个属性。
注解实例
如果不使用 xml 的方式,使用注解也可取得同样的效果,如下:
1  | 
  | 
insert 属性
insert 标签也支持诸多属性来改变语句的行为。
其中常见且重要的属性如下表:
| 属性 | 描述 | 
|---|---|
| id | 在命名空间中的唯一标识符 | 
| parameterType | 语句的参数类型,默认可选,MyBatis 会自动推断 | 
| flushCache | 设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认为 false | 
| timeout | 设置超时时间 | 
| statementType | STATEMENT,PREPARED 或 CALLABLE 中的一个,默认为 PREPARED(预处理) | 
| useGeneratedKeys | 取出由数据库自动生成的主键,仅对支持主键自动生成的数据库有效,默认为 false | 
| keyProperty | 主键的名称,必须与useGeneratedKeys 一起使用,默认未设置 | 
返回主键
select 标签在需要返回被添加记录的主键时,会稍微复杂一点。
自增主键
xml 方式
如果使用的数据库,如 MySQL,PostgreSQL,这些数据库支持自增主键,那么得到返回的主键只需添加上 useGeneratedKeys 和 keyProperty 两个属性即可。如下:
1  | <insert id="insertUserNoId" useGeneratedKeys="true" keyProperty="id"  | 
在 insertUserNoId 中,我们并未添加上 id 参数,而是使用了数据库自增主键的特性,keyProperty 属性值对应 id 字段的名称,这样当语句执行成功后,对象的 id 字段会被自动设置为返回的 id 值。
注解方式
使用下面的注解方式,同样可以实现同样的效果:
1  | 
  | 
MyBatis 提供了 Options 注解来指定方法调用的行为。
selectKey 标签
xml 方式
如果使用的数据库不支持主键自增,如 Oracle,MyBatis 提供了 selectKey 标签来通过 SQL 语句获得主键。
例如:
1  | <insert id="insertUserNoId" parameterType="com.ahao.mybatis.model.User">  | 
selectKey 标签必须在 insert 标签里面,selectKey 有 4 个属性,它们的作用如下表:
| 属性 | 描述 | 
|---|---|
| keyColumn | 数据库字段名,对应返回结果集中的名称 | 
| keyProperty | 目标字段名称,对应Java 对象的字段名 | 
| resultType | id字段的类型 | 
| order | 执行的顺序,在 insert 之前调用为 BEFORE,之后为 AFTER | 
注意,selectKey 中的语句其实就是 SQL 语句,不同数据库得到主键的语句均不一样。
注解方式
1  | 
  | 
selectKey 也有相应的注解,不过配置属性略有不同,statement 属性对应标签中的 SQL 语句,而 before 属性则对应标签中的 order 属性,若 before 为 false,则 order 对应为 AFTER。
实践
下面,我们一起来实操巩固一下。
例1. 插入用户
请使用 MyBatis 完成在 imooc_user 表中插入用户的功能。
分析:
按照 MyBatis 的开发模式,先在对应 UserMapper.xml 文件中添加插入用户的 insert 标签,然后在 UserMapper.java 中增加上对应的方法即可。
步骤:
使用 MyBatis 向数据库中插入用户。由于我们使用的数据库是 MySQL,因为直接使用 useGeneratedKeys 得到自增主键。
首先,我们在 UserMapper.xml 文件中添加上对应的 insert 标签:
1  | <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"  | 
然后在 UserMapper.java 中添加对应的方法:
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 insertUser 方法:
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
TIPS: 注意,这里必须通过 commit 方法提交会话,语句才会生效。
小结
- 绝大多数情况下,我们都不会在插入时指定 id 的值,而是通过数据库自动去生成。
 - 不同数据库获得主键的 SQL 语句也不同,如果通过 selectKey 获得主键,那么一定要注意数据库厂商之间的差异性。
 
MyBatis update
前言
本小节,我们将一起学习 MyBatis update。
在 MyBatis 中,update 标签对应于 SQL 语句中的 update 更新。
定义
update 标签用于映射 SQL 中的
更新语句。
实例
xml 实例
如下就是一个真实的 update 标签实例。
1  | <update id="updateUserAgeById">  | 
每一个 update 标签都必须有一个唯一的 id 属性,在 update 标签内部则是一条 SQL 语句。
注解实例
使用如下的注解方式,我们也可以实现同样的功能。
1  | 
  | 
update 属性
update 标签支持一些属性来改变更新语句的行为。
其中常见且重要的属性如下表:
| 属性 | 描述 | 
|---|---|
| id | 在命名空间中的唯一标识符 | 
| parameterType | 语句的参数类型,默认可选,MyBatis 会自动推断 | 
| flushCache | 设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认为 false | 
| timeout | 设置超时时间 | 
| statementType | STATEMENT,PREPARED 或 CALLABLE 中的一个,默认为 PREPARED(预处理) | 
实践
例1. 更新用户年龄
请使用 MyBatis 完成对 imooc_user 表中用户年龄更新的功能。
分析:
按照 MyBatis 的开发模式,先在对应 UserMapper.xml 文件中添加用户年龄更新的 update 标签,然后在 UserMapper.java 中增加上对应的方法即可。
步骤:
首先,在 UserMapper.xml 中添加 update 标签,并在标签中写入 SQL :
1  | <update id="updateUserAgeById">  | 
然后在 UserMapper.java 中添加上对应的接口方法,方法接受 age 和 id 两个参数。
1  | package com.ahao.mybatis.mapper;  | 
注意:这里我们使用了@Param这个注解,由于在 update 标签中有两个参数 age 和 id,我们需要通过 @Param 来告诉 MyBatis 参数的对应关系。
结果:
通过如下代码,我们运行 updateUserAgeById 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,id 为 1 的用户年龄已经被更新为 180。
1  | +----+-------------+-----+--------+  | 
例2. 更新用户名称和分数
请使用 MyBatis 完成对 imooc_user 表中用户名称和分数更新的功能。
分析:
同上。
步骤:
首先,在 UserMapper.xml 中添加另一个 update 标签,并在标签中写入 SQL :
1  | <update id="updateUsernameAndScoreById">  | 
然后在 UserMapper.java 中添加上对应的接口方法,方法接受 username、score 和 id 三个参数。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 updateUsernameAndScoreById 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,id 为 1 的用户信息已被更改。
1  | +----+-------------+-----+--------+  | 
小结
- update 标签并无太多的知识点,主要的工作量在书写 SQL 上,因此良好的 SQL 功底可以帮助你更加快速的上手 MyBatis。
 
MyBatis delete
前言
本小节,我们将一起学习 MyBatis delete。
在 MyBatis 中,delete 标签对应于 SQL 语句中的 delete 删除。
定义
delete 标签用于映射 SQL 中的
删除语句。
实例
xml 实例
如下,是一个真实的 delete 标签实例。
1  | <delete id="deleteUserById">  | 
每一个 delete 标签都必须有一个唯一的 id 属性,在 delete 标签内部则是一条 SQL 语句。
注解实例
上面的 delete 标签对应的注解实例如下:
1  | 
  | 
delete 属性
delete 标签支持一些属性来改变更新语句的行为。
其中常见且重要的属性如下表:
| 属性 | 描述 | 
|---|---|
| id | 在命名空间中的唯一标识符 | 
| parameterType | 语句的参数类型,默认可选,MyBatis 会自动推断 | 
| flushCache | 设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认为 false | 
| timeout | 设置超时时间 | 
| statementType | STATEMENT,PREPARED 或 CALLABLE 中的一个,默认为 PREPARED(预处理) | 
实践
例1. 根据 id 删除用户
请使用 MyBatis 完成对 imooc_user 表中通过 id 删除用户的功能。
分析:
按照 MyBatis 的开发模式,先在对应 UserMapper.xml 文件中添加根据 id 删除用户的 delete 标签,然后在 UserMapper.java 中增加上对应的方法即可。
步骤:
首先,在 UserMapper.xml 中添加 delete 标签,并在标签中写入 SQL :
1  | <delete id="deleteUserById">  | 
然后在 UserMapper.java 中添加上对应的接口方法,方法接受 id 一个参数。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 deleteUserById 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,id 为 10 的用户已被删除。
例2. 根据用户名删除用户
请使用 MyBatis 完成对 imooc_user 表中通过用户名删除用户的功能。
分析:
同上。
步骤:
首先,在 UserMapper.xml 中添加 delete 标签,并在标签中写入 SQL :
1  | <delete id="deleteUserByName">  | 
然后在 UserMapper.java 中添加上对应的接口方法,方法接受 username 一个参数。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 deleteUserByName 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,用户名为 tom 的用户已被删除。
小结
- delete 标签并无太多的知识点,主要的工作量在书写 SQL 上。
 
OGNL 表达式
前言
MyBatis 的动态 SQL 广泛应用到了OGNL 表达式,OGNL 表达式可以灵活的组装 SQL 语句,从而完成更多的功能。OGNL 易学易用,与 Java 代码几乎一致,本小节我们将系统的介绍 OGNL 表达式在 MyBatis 中的使用。
定义
OGNL 全称 Object-Graph Navigation Language,是 Java 中的一个开源的表达式语言,用于访问对象数据。
介绍
实例
OGNL 最常用于 if 标签中,用于判断条件是否满足,如下:
1  | <if test="age != null">  | 
if 标签的 test 属性就是个典型的 OGNL 表达式。age != null表示当 age 不为 null 的时候 if 成立,则动态的向 SQL 中插入标签内的 SQL 代码段。
常见的 OGNL 表达式
在 MyBatis 中常见的 OGNL 表达式如下:
- e1 or e2:或关系
 - e1 and e2:与关系
 - e1 == e2 或者 e1 eq e2:相等
 - e1 != e2 或者 e1 neq e2:不等
 - e1 lt e2 ;e1 < e2;e1 gt e2;e1 > e2;e1 lte e2;e1 <= e2;e1 gte e2;e1 >= e2:比较关系
 - e1 + e2;e1 - e2;e1 * e2;e1 / e2;e1 % e2:运算关系
 - !e 或者 not e:非,取反
 - e.method(args):调用对象方法
 - e.property:访问属性值
 - e1[e2]:访问数组、链表(e2 为序号)或者 Map(e2 为键值)
 
其中 1~4 以及 9~10 都是特别常用的几种情况,而其它的情况不利于 SQL 的维护,因此并不常见。
TIPS: 提示, 如果你熟悉 Python 的话,会发现 OGNL 表达式完全就是在写 Python。
实践
下面我们就来以实例来看一看 OGNL 表达式。
有一个名为 pedro 的 User 对象,如下:
1  | User pedro = new User();  | 
访问属性
访问用户的 username 属性,OGNL 表达式为pedro.username,结果为:
1  | # pedro.username  | 
访问列表
访问用户的第一个标签,OGNL 表达式为pedro.tags[0],结果为:
1  | # pedro.tags[0]  | 
比较
比较用户标签长度是否大于 1,OGNL 表达式为pedro.tags[0],结果为:
1  | # pedro.tags.size > 1  | 
运算
用户年龄加上一个整数 22,OGNL 表达式为pedro.age + 22,结果为:
1  | # pedro.age + 22  | 
方法调用
将用户年龄全部大写,OGNL 表达式为pedro.username.toUpperCase,结果为:
1  | # pedro.username.toUpperCase  | 
小结
- OGNL 表达式是 MyBatis 动态 SQL 的核心,小巧精致却功能强大,易学易用。
 
MyBatis if 和多数据库支持
前言
动态 SQL 是 MyBatis 最标志性的特性之一。在其它框架中,你可能需要根据不同的条件来拼接 SQL,辗转在符号与条件的判断上,处理起来麻烦而且易错,而 MyBatis 的动态 SQL 可以让我们摆脱这种痛苦,简单而又高效的书写 SQL。
MyBatis 动态 SQL 由 OGNL 表达式和条件标签两部 分组成,我们将会分为多个小节进行介绍。
OGNL 表达式是动态 SQL 的基础,如果你还不了解,请务必点击学习一下。条件标签部分我们将会在四个小节中分别介绍,它们分别是MyBatis if 和多数据库支持(本小节),MyBatis choose和bind小节,MyBatis where、set、trim小节和MyBatis foreach小节。
本小节,我们先来学习最基础的条件标签 if,以及如何使用 if 来让 MyBatis 来支持多数据库。
定义
if常用于 where 语句中,通过判断参数来决定在 select 语句中是否使用某个查询条件,或者在 update 语句中判断是否更新一个字段,还可以在 insert 语句中决定是否插入一个满足条件的值。
实例
我们以一个实际的例子来看一下 if 是如何工作的。
1  | <select id="selectUserByAgeAndScore" parameterType="com.ahao.mybatis.model.User" resultMap="userMap">  | 
在 if 标签中,test 属性是 OGNL 的判断表达式。
selectUserByAgeAndScore 的作用是通过用户年龄和积分来查询用户,当 score 值为 null 时,if 判断不成立,此时生成的 SQL 语句为:
1  | SELECT * FROM imooc_user WHERE age = ?  | 
而当 if 成立时,生成的 SQL 语句为:
1  | SELECT * FROM imooc_user WHERE age = ? AND score = ?  | 
通过这样条件判断方式,MyBatis 能根据实际情况来动态生成 SQL 语句。
实践
例1、动态查询用户
请使用 MyBatis 完成对 imooc_user 表动态查询用户的功能, username为必须的查询条件,年龄和积分若为 null 则不使用该字段进行过滤。
分析:
按照 MyBatis 的开发模式,先在 UserMapper.xml 文件中添加动态查询用户的 select 标签,然后在 UserMapper.java 中增加上对应的方法。
步骤:
首先,在 UserMapper.xml 中添加 select 标签,并在标签中写入 SQL,使用 if 来判断属性值是否为 null,若为 null,则不使用该字段进行查询。如下:
1  | <select id="selectUserByNameCondition" parameterType="com.ahao.mybatis.model.User"  | 
通过 if 对属性的判断,SQL 的过滤条件就会发生相应的变化。
然后在 UserMapper.java 中添加上对应的接口方法,方法接受 User对象作为参数。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 selectUserByNameCondition 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
condition 对象 username 和 score 属性均不为空,而 age 属性为空,因此最后所执行的 SQL 为:
1  | SELECT * FROM imooc_user WHERE username = ? AND score = ?  | 
成功后,结果为:
1  | User{id=2, username='pedro', age=24, score=200}  | 
多数据库支持
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于配置文件中的 databaseId。
配置
首先在 MyBatis 的全局配置文件中添加如下配置:
1  | 
  | 
在 configuration 中加入 databaseIdProvider 后,还需要在 databaseIdProvider 标签中添加上需要使用到的数据库名称,如:SQL Server。每一个 property 属性都代表了一个数据库,name 表示数据库厂商名称,value 用来设置别名。
1  | 
  | 
使用
配置完毕后,我们就可以在 mapper xml 文件中,通过 if 来判断当前的数据库厂商,从而动态生成不同数据库的 SQL。
例如,PostgreSQL 支持使用 ||来拼接字符串,而 MySQL 需要使用 concat函数来拼接字符串。
因此,如果在模糊查询的时候,不同的数据库厂商需要不同的 SQL 语句,通过 if 来判断数据库厂商来生成对于的 SQL 语句。
如下:
1  | <select id="selectUserByLikeName" resultType="com.ahao.mybatis.model.User">  | 
注意,这里 _databaseId 参数是由 MyBatis 提供的内置参数,对应于 databaseIdProvider 中配置的数据库名称。
通过 databaseIdProvider 配置,即时数据库厂商之间存在差异,但仍然可以通过动态 SQL 的形式来支持多数据库。
小结
- if 简单且好用,是动态 SQL 中的最基础的标签,一看便会。
 - 多数据库支持的应用场景其实较少,但如果真的需要,也可通过 MyBatis 来方便的实现。
 
MyBatis choose 和 bind
前言
在上一小节中,我们介绍了 MyBatis 动态 SQL 中最基础的标签——if 标签,本小节我们将一起学习 choose 和 bind 标签。
choose 标签是 if 标签的增强版,适用于更加复杂的条件判断逻辑;而bind 标签则可以在 OGNL 上下文环境中新绑定一个变量,供后面的 SQL 使用。
定义
choose标签相当于编程语言 if…else 语句,用于动态 SQL 中的多条件判断,是 if 标签的增强版。
bind标签可以在动态 SQL 中通过 OGNL 表达式来创建一个新的变量绑定到上下文中,供后续的 SQL 使用。
实例
下面是一个融合了 choose 和 bind 标签的实例:
1  | <select id="selectUserByLikeName" resultType="com.ahao.mybatis.model.User">  | 
通过 choose 标签来判断当前的数据库厂商,如果是 MySQL 数据库,则调用CONCAT函数来拼接 % 和 username,如果是 PostgreSQL 数据库,则使用操作符||来拼接,如果是其它类型的数据库,则直接通过 OGNL 表达式来绑定一个新的变量 usernameLike。
在这个例子中,choose 是一个条件选择标签,第一个 when 相当于 if 判断,第二个 when 相当于 else if,最后的 otherwise 相当于 else。比起 if 标签,choose 标签无疑更为易用,适用于同一条件的多次判断逻辑。
实践
例1. 多条件查询用户
请使用 MyBatis 完成对 imooc_user 表多条件查询用户的功能,如果 id 不为空则直接使用 id 查询用户,否则使用 username 查询用户,如果 username 也为空,则直接查询全部用户。
分析:
按照 MyBatis 的开发模式,需先在 UserMapper.xml 文件中添加多条件查询用户的 select 标签,然后在 UserMapper.java 中添加上对应的方法。
步骤:
首先,在 UserMapper.xml 中添加 select 标签,并在标签中写入 SQL,使用 choose 中的 when 标签来依次判断 id 和 username 是否为 null,若均为 null,则在 otherwise 中添加上一个永假的值。如下:
1  | <select id="selectUserByIdOrName" resultType="com.ahao.mybatis.model.User">  | 
这里使用了1 = 0作为条件判断的永假值。
然后在 UserMapper.java 中添加上对应的接口方法,方法接受 id 和 username 两个参数,都可为空。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 selectUserByIdOrName 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
id 和 username 两个属性均为空,因此所执行的 SQL 为:
1  | SELECT * FROM imooc_user WHERE 1 = 0  | 
成功后,结果为:
1  | null  | 
例2. 查询小写名称客户
请使用 MyBatis 完成对 imooc_user 表查询小写名称客户的功能,将名称小写后再进行查询。
分析:
同上。
步骤:
首先,在 UserMapper.xml 中添加 select 标签,并在标签中写入 SQL,使用 bind 标签将参数小写化成一个新的变量 lowercaseName。
1  | <select id="selectUsernameLowercase" resultType="com.ahao.mybatis.model.User">  | 
然后在 UserMapper.java 中添加上对应的接口方法,方法接受 username 一个参数。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 selectUsernameLowercase 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,结果为:
1  | User{id=2, username='pedro', age=24, score=200}  | 
小结
- bind 标签虽然可以通过 OGNL 表达式在上下文中绑定一个新的变量,但在实际应用所见不多,我们希望在代码层面上处理好参数,这样既方便维护,也更有利于迁移。
 - choose 标签是一个增强版的 if 标签,适用于更加复杂的逻辑判断场景。
 
MyBatis where、set、trim
前言
在前面的小节中,我们一起学习了 if 标签,在通过 if 判断动态插入或查询的时候容易产生多余的前缀(如 WHERE)或后缀(如 , ),为了解决这类问题 MyBatis 提供了 where、set、trim 三个实用的标签。
其中 where 和 set 标签都是 trim 标签的一种特殊情况,不过使用频率非常高,因此被单独定义成了标签。本小节我们将一起来学习它们。
定义
where、set、trim三个标签都是为了解决 MyBatis 在动态生成 SQL 时,产生了多余的前缀和后缀的问题。
实例
where 实例
1  | <select id="selectUserByIdAndName" resultType="com.ahao.mybatis.model.User">  | 
在这个例子中,id 和 username 均非固定的过滤参数,只有当其不为 null 时才会加入到 SQL 语句中。此处 where 标签的作用大概有如下 3 个:
- 在 id 和 username 都为空的情况下,where 标签不会产生任何 SQL 代码段,最后的 SQL 语句为:
SELECT * FROM imooc_user; - 在 id 不为空,username 为空的情况下,或者在 id 不为空,username 也不为空的情况下,where 标签会自动给 SQL 代码段添加上 WHERE 前缀;
 - 在 id 为空,username 不为空的情况下,where 标签会自定去除 AND 前缀,此时生成的 SQL 语句为: 
SELECT * FROM imooc_user WHERE username = ?。 
set 实例
1  | <update id="updateUsernameAndScoreById">  | 
set 标签会自动在 SQL 语句中添加上相应的 SET 前缀,并根据里面的条件动态地添加相应的字段。
由于 SQL update 的语法问题,若 set 标签里面条件均不满足,此时 set 标签也不会添加上 SET 前缀,但此时 SQL 会报语法错误,所以 set 标签中还是得有一个必然存在的赋值。
trim 实例
1  | <update id="updateUsernameAndScoreById">  | 
这里的 trim 实例实现了与 set 实例同样的功能,set 标签是 trim 标签的一种特殊情况。
trim 标签共有 4 个属性,它们的作用分别如下:
- prefix: 前缀属性,若标签内不为空则在 SQL 中添加上前缀;
 - prefixOverrides: 前缀覆盖属性,若标签中有多余的前缀,将会被覆盖(其实就是丢弃该前缀);
 - suffix: 后缀属性,若标签内不为空则在 SQL 中添加上后缀;
 - suffixOverrides: 后缀覆盖属性,若标签中有多余的后缀,将会被覆盖(其实就是丢弃该后缀)。
 
这个例子中,trim 标签的前缀为SET,后缀覆盖属性为,,所以在标签内不为空的情况下给 SQL 语句添加了 SET 关键字,且标签内若有多余的,后缀也会被丢弃。
实践
例1. 动态插入用户
请使用 MyBatis 完成对 imooc_user 表动态插入用户的功能,若用户属性值不为 null 则插入该字段,否则不插入字段。
分析:
按照 MyBatis 的开发模式,先在 UserMapper.xml 文件中添加动态插入用户的 insert 标签,然后在 UserMapper.java 中添加上对应的方法。
步骤:
首先,在 UserMapper.xml 中添加 insert 标签,并在标签中写入 SQL,使用 trim 标签来动态判断属性是否为空,若不为空则插入该字段。
1  | <insert id="insertUserDynamic" parameterType="com.ahao.mybatis.model.User">  | 
这里,我们使用了两个 trim 标签,一个用来动态写入字段名,另一个用来动态写入字段值;trim 标签的前缀为(,后缀为),当出现多余的,时,会通过后缀覆盖属性来丢弃。
然后在 UserMapper.java 中添加上对应的接口方法:
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 insertUserDynamic 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,结果为:
1  | 1  | 
TIPS: 注意,在 username、score 和 age 均为 null 时, insertUserDynamic 会报 SQL 语法错误,但理论上若是所有属性均为空,那么也不应该插入。
小结
- where、set 以及 trim 将 MyBatis 的动态 SQL 发挥到了极致,为开发者节省了大量的精力和时间。
 - trim 标签是 MyBatis 中最为强大的一个标签,使用它可以方便的完成 SQL 动态插入和动态更新。
 
MyBatis foreach
前言
在 MyBatis 中,常常会遇到集合类型的参数,虽然我们可以通过 OGNL 表达式来访问集合的某一个元素,但是 OGNL 表达式无法遍历集合。foreach 标签就是专门用来解决这类问题的,本小节我们就来一起学习它。
定义
foreach标签用来遍历数组、列表和 Map 等集合参数,常与 in 关键字搭配使用。
实例
我们以 3 个例子来看一看 foreach 是如何遍历列表、数组和 Map 的。
遍历列表
xml:
1  | <select id="selectUserInIds" resultType="com.ahao.mybatis.model.User">  | 
Java:
1  | List<User> selectUserInIds(List<Integer> ids);  | 
上面是 selectUserInIds 方法在 java 和 xml 中对应的代码段。
foreach 标签共有 6 个属性,它们的作用分别为:
- collection: 被遍历集合参数的名称,如 list;
 - open: 遍历开始时插入到 SQL 中的字符串,如 ( ;
 - close: 遍历结束时插入到 SQL 中的字符串,如 ) ;
 - separator: 分割符,在每个元素的后面都会插入分割符;
 - item: 元素值,遍历集合时元素的值;
 - index: 元素序列,遍历集合时元素的序列。
 
当 selectUserInIds 方法的参数 ids 为Arrays.asList(1, 2)时,生成的 SQL 语句为:
1  | SELECT * FROM imooc_user WHERE id IN ( 1 , 2 )  | 
foreach 标签的 collection 属性在接受参数名有两种情况:一、匿名参数,当在 java 方法中没有通过 @Param 注解指定参数名时,列表类型的使用默认参数名 list。二、具名参数,java 方法中使用了@Param 注解指定了参数名称,则 foreach 中的 collection 属性必须为参数名,如:
1  | List<User> selectUserInIds( List<Integer> ids);  | 
我们推荐你为列表类型参数用注解指定一个名称,让使用该名称来遍历,方便代码维护和阅读。
遍历数组
当 Java 方法使用的参数类型为数组时,如下:
1  | List<User> selectUserInIds(Integer[] ids);  | 
如果 ids 参数使用 @Param 注解指定了参数名称,则 foreach 标签中的 collection 属性必须为该名称;但若未指定名称,则在 foreach 标签中使用默认数组名称 array,如下:
1  | <select id="selectUserInIds" resultType="com.ahao.mybatis.model.User">  | 
遍历 Map
当 Java 方法使用的参数类型为 Map 时,如下:
1  | int updateUserById( Map map, Integer id);  | 
使用 foreach 标签遍历 Map 时,collection 属性值为注解指定的参数名,即 params,且 item 是 Map 的键值,index 是键名。
1  | <update id="updateUserById">  | 
注意: 由于 key 是字段名称,因此不能使用#{}作为占位符,只能使用${}在字符串中替换。
updateUserById 生成的 SQL 语句大致如下:
1  | UPDATE imooc_user SET score = ? , age = ? WHERE id = ?  | 
实践
例1. 使用名称批量查询用户
请使用 MyBatis 完成对 imooc_user 表使用名称批量查询用户的功能,参数为一个名称列表,使用 in 关键字进行查询。
分析:
按照 MyBatis 的开发模式,先在 UserMapper.xml 文件中添加使用名称批量查询用户的 select 标签,然后在 UserMapper.java 中添加上对应的方法。
步骤:
首先,在 UserMapper.xml 中添加 select 标签,并在标签中写入 SQL,使用 foreach 标签来遍历名称列表。
1  | <select id="selectUserInNames" resultType="com.ahao.mybatis.model.User">  | 
然后在 UserMapper.java 中添加上对应的接口方法:
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 selectUserInNames 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,结果为:
1  | [User{id=1, username='peter', age=18, score=100}, User{id=2, username='pedro', age=24, score=200}]  | 
例2. 批量插入用户
请使用 MyBatis 完成对 imooc_user 表批量插入用户的功能,参数为一个用户列表。
分析:
同上。
步骤:
首先,在 UserMapper.xml 中添加 insert 标签,并在标签中写入 SQL,使用 foreach 标签来遍历用户列表。
1  | <insert id="insertUsers">  | 
注意,这里遍历 users 得到的单位是 user,user 是一个对象,因此必须通过 OGNL 表达式来取 user 的属性。
然后在 UserMapper.java 中添加上对应的接口方法:
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 insertUsers 这个方法。
1  | User user1 = new User();  | 
成功后,结果为:
1  | 2  | 
小结
- foreach 标签是使用非常广泛的一个标签,当使用 SQL 进行批量插入、查询时都需要使用到它。
 - 列表遍历的使用最为广泛,数组和 Map 则相对较少。
 
MyBatis script
前言
前面一系列动态 SQL 小节的学习中,我们都是在 xml 中书写 SQL 的。注解无法发挥 MyBatis 动态 SQL 的真正威力,但是 if、choose、bind、where 等标签还是可以在注解中使用的。
MyBatis 官方文档对于此的介绍只有寥寥一句话和一个简单的例子,在实际的应用中也几乎没有人这样去做,因为它确实不太美观,但是考虑到这个知识点并不复杂,也极有可能成为一个刁钻的面试点,我们还是一起来学习一下。
实例
在注解中使用动态 SQL 其实十分简单,只需在动态 SQL 语句的外面包上一层script标签即可。如下:
1  | 
  | 
在 Select 注解中,我们没有直接写入 SQL,而是在最外层套上一个 script 标签,这里考虑到 SQL 语句的美观性,我们把语句分成了字符串数组来书写,MyBatis 会自动将其拼接成一个完整的语句。
实践
例1. 查询小写名称客户
请使用 MyBatis 完成对 imooc_user 表查询小写名称客户的功能,将名称小写后再进行查询。
分析:
使用本小节所学的知识,直接在 UserMapper.java 接口上添加方法,并使用 Select 注解即可。
步骤:
在 UserMapper.java 中添加上对应的接口方法,方法接受 username 一个参数。
1  | package com.ahao.mybatis.mapper;  | 
结果:
通过如下代码,我们运行 selectUsernameLowercase 这个方法。
1  | UserMapper userMapper = session.getMapper(UserMapper.class);  | 
成功后,结果为:
1  | User{id=2, username='pedro', age=24, score=200}  | 
小结
- 通过 scirpt 标签,我们可以在注解中使用动态 SQL 的诸多标签,极大地增强了注解的能力,但相对于 xml 这种更为优雅的方式,无疑是后者更佳,因此我们我们强力推荐你使用 xml 的方式。
 
MyBatis 配置介绍
前言
MyBatis 的配置十分重要,它直接左右 MyBatis 的行为。我们可以将 MyBatis 配置分为两大部分,第一部分是 mapper,也就是容纳 SQL 语句的.xml文件,另一部分是 configuration ,也就是前面小节提到的 mybatis-config.xml 文件。
本小节,我们将介绍 configuration 中常见且有用的配置项。
结构
MyBatis 以 .xml 作为配置文件,且以 configuration 作为配置的根节点。在 configuration 下有诸多配置项,它们的结构如下:
configuration(配置)
- properties(属性)
 - settings(设置)
 - typeAliases(类型别名)
 - typeHandlers(类型处理器)
 - objectFactory(对象工厂)
 - plugins(插件)
 - environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
 - dataSource(数据源)
 
 
 - environment(环境变量)
 - databaseIdProvider(数据库厂商标识)
 - mappers(映射器)
 
MyBatis configuration 共 9 项,其中一些配置在前面的小节中已经介绍到了,下面我们将分别介绍每一项配置。
属性 properties
通过 properties 配置,我们可以将一些重要的配置属性抽离到其它的 .properties 文件。
比如,dataSource 中的数据库 url、用户名和密码,我们可以单独以 datasource.properties 文件来存储,然后在 mybatis-config.xml 文件中导入使用。
在 resources 目录下新建 datasource.properties 文件,并填入以下内容:
1  | url=jdbc:mysql://localhost:3306/imooc?useSSL=false  | 
然后在 mybatis-config.xml 文件中通过 properties 配置来引入 datasource.properties 文件:
1  | 
  | 
通过 properties 中的 resource 属性引入 datasource.properties 后,我们就可以使用占位符的方式去动态替换配置,如 ${url},表示从 datasource.properties 文件中取出 url 项并填充在此处。
它们在目录中的位置如下:
1  | src/main/resources  | 
设置 settings
MyBatis 提供了 settings 来设置一些主要的参数,它们会直接的改变 MyBatis 的运行时行为。
settings 共有十几项,我们罗列一些常用的:
| 设置名 | 描述 | 可选值 | 默认值 | 
|---|---|---|---|
| cacheEnabled | 全局地开启或关闭所有 mapper 中的缓存 | true | false | true | 
| lazyLoadingEnabled | 延迟加载的全局开关,当开启时,所有关联对象都会延迟加载 | true | false | false | 
| defaultStatementTimeout | 设置数据库查询超时时间 | 任意正整数 | null | 
| mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射 | true |false | false | 
| localCacheScope | MyBatis会默认缓存会话中的查询,即 SESSION,若无需缓存则设置为 STATEMENT | SESSION | STATEMENT | SESSION | 
| defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler | Java 类的全路径 | org.apache.ibatis. type.EnumTypeHandler | 
| logPrefix | 指定 MyBatis 日志名称前缀 | 任何字符串 | 未设置 | 
| logImpl | 指定 MyBatis 日志的实现,未指定时将自动查找 | SLF4J | LOG4J|LOG4J2|JDK_LOGGING|COMMONS_LOGGING|STDOUT_LOGGING|NO_LOGGING | 未设置 | 
| proxyFactory | 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具 | CGLIB | JAVASSIST | JAVASSIST | 
当使用它们时,你只需要在 mybatis-config.xml 配置文件中打开相应的配置。
例如,我们开启了下划线转驼峰的配置:
1  | 
  | 
别名 typeAliases
MyBatis 在指定 Java 类时需要使用到类的全路径,如 com.ahao.mybatis.model.Blog,typeAliases 可以为全路径定义一个别名,这样就能减少一定的重复工作。
例如,将 com.ahao.mybatis.model.Blog 的别名定义为 Blog:
1  | 
  | 
MyBatis 还支持为一个包下所有类定义别名:
1  | 
  | 
这样在 com.ahao.mybatis.model 包中的所有类都有了别名,每个类的别名都是其类的名称首字母小写,如 Author 类的别名为 author。
类型处理器 typeHandlers
类型处理器我们将在类型处理器小节中再详细介绍。
对象工厂 objectFactory
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)来完成。MyBatis 默认的对象工厂仅仅只是实例化目标类,我们可以自定义一个对象工厂类来覆盖默认的对象工厂。
配置如下:
1  | 
  | 
绝大多数情况下,这个操作都是极其危险的,改变了 MyBatis 默认的对象创建行为可能会带来一定的兼容错误,所以我们不做过多介绍,如果你确实需要它,可以查阅相关的资料。
插件 plugins
插件我们将在插件小节中再详细介绍。
环境配置 environments
环境配置是最为复杂的一项配置,MyBatis 提供了多环境配置机制,例如:开发环境和生产环境上的数据库配置就大概率不一样。
每个 environment 都有一个唯一的 id 字段,且 environments 需要提供一个默认环境,如下:
1  | 
  | 
在每个 environment 下又有两个子配置项,它们分别负责管理事务和数据源。
事务管理器 transactionManager
在 xml 文件中对应 <transactionManager type="JDBC"/>,其中 type 属性对应了事务管理器的两种类型,分别是JDBC和MANAGED。
- JDBC :直接使用了 JDBC 的提交和回滚机制。
 - MANAGED:让容器来管理事务的整个生命周期,例如 spring 容器。
 
提示: 如果你使用 spring 作为容器,那么 transactionManager 会被自动配置且可用。
数据源 dataSource
在 xml 文件中对应<dataSource type="POOLED">,其中 type 属性代表了数据源的类型,可选的有三种类型,如下:
- UNPOOLED:非池化数据源,每次使用时打开,结束后关闭,不推荐。
 - POOLED:池化数据源,连接池管理连接,推荐。
 - JNDI:在 EJB 这类容器中使用,几乎不用。
 
数据库厂商标识 databaseIdProvider
多数据源支持我们将在多数据源支持小节中详细介绍。
映射器 mappers
通过 mappers 配置,我们可以指定所对应 SQL 映射文件,这样 MyBatis 才能找到另一部分的 SQL 配置文件。
mappers 可以包含多个 mapper,mapper 的加载共有 4 种方式。
相对类路径
通过 resource 属性指定 mapper .xml 文件所对应的类路径。
1  | <mappers>  | 
URL路径
通过 url 属性指定 mapper .xml 文件所对应的文件路径。
1  | <mappers>  | 
类路径
通过 class 属性指定 mapper 类所对应的类路径。
1  | <mappers>  | 
包路径
通过制定包路径,将包中的所有接口类自动扫描为 mapper。
1  | <mappers>  | 
小结
- MyBatis 的配置是比较多的,本小节列举了一些
常用且重要的配置,如果你还不满足,可以阅读这里的官方配置文档。 - MyBatis 的另一部分 SQL 配置虽然分散在了包中,但通过 mappers 这个中间桥梁,二者又紧密的结合在一起了。
 - 在真实的开发中,会有专门的类库来提供这些配置,但你需要了解它们,以便在需要时迅速作出反应。
 






