biancheng-Mybatis框架
目录http://c.biancheng.net/mybatis/
1MyBatis是什么
2MyBatis和Hibernate的区别
3MyBatis下载
4第一个MyBatis程序
5MyBatis核心对象
6MyBatis配置文件
7MyBatis映射器
8MyBatis执行SQL的两种方式
9MyBatis select标签
10MyBatis insert标签
11MyBatis update标签
12MyBatis delete标签
13MyBatis resultMap元素
14MyBatis注解
15MyBatis关联查询
16MyBatis一对一关联查询
17MyBatis一对多关联查询
18MyBatis多对多关联查询
19MyBatis动态SQL
20MyBatis if标签
21MyBatis choose、when和otherwise标签
22MyBatis where标签
23MyBatis set标签
24MyBatis foreach标签
25MyBatis bind标签
26MyBatis trim标签
27MyBatis分页
28MyBatis缓存
29MyBatis逆向工程
MyBatis 支持定制化 SQL、存储过程以及高级映射,可以在实体类和 SQL 语句之间建立映射关系,是一种半自动化的 ORM 实现。其封装性低于 Hibernate,但性能优秀、小巧、简单易学、应用广泛。
ORM(Object Relational Mapping,对象关系映射)是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,并且提供了一种机制,通过 JavaBean 对象去操作数据库表中的数据。
MyBatis 的主要思想是将程序中的大量 SQL 语句剥离出来,使用 XML 文件或注解的方式实现 SQL 的灵活配置,将 SQL 语句与程序代码分离,在不修改程序代码的情况下,直接在配置文件中修改 SQL 语句。
MyBatis 与其它持久性框架最大的不同是,MyBatis 强调使用 SQL,而其它框架(例如 Hibernate)通常使用自定义查询语言,即 HQL(Hibernate查询语言)或 EJB QL(Enterprise JavaBeans查询语言)。
MyBatis 官方文档:https://mybatis.org/mybatis-3/zh/
优点
- MyBatis 是免费且开源的。
- 与 JDBC 相比,减少了 50% 以上的代码量。
- MyBatis 是最简单的持久化框架,小巧并且简单易学。
- MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 中,和程序逻辑代码分离,降低耦合度,便于同一管理和优化,提高了代码的可重用性。
- 提供 XML 标签,支持编写动态 SQL 语句。
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
- 支持存储过程。MyBatis 以存储过程的形式封装 SQL,可以将业务逻辑保留在数据库之外,增强应用程序的可移植性、更易于部署和测试。
缺点
- 编写 SQL 语句工作量较大,对开发人员编写 SQL 语句的功底有一定要求。
- SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
使用场景
MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。适用于性能要求高,且需求变化较多的项目,如互联网项目。
MyBatis和Hibernate的区别
Hibernate 和 MyBatis 都是目前业界中主流的对象关系映射(ORM)框架,它们的主要区别如下。
1)sql 优化方面
- Hibernate 使用 HQL(Hibernate Query Language)语句,独立于数据库。不需要编写大量的 SQL,就可以完全映射,但会多消耗性能,且开发人员不能自主的进行 SQL 性能优化。提供了日志、缓存、级联(级联比 MyBatis 强大)等特性。
- MyBatis 需要手动编写 SQL,所以灵活多变。支持动态 SQL、处理列表、动态生成表名、支持存储过程。工作量相对较大。
2)开发方面
- MyBatis 是一个半自动映射的框架,因为 MyBatis 需要手动匹配 POJO 和 SQL 的映射关系。
- Hibernate 是一个全表映射的框架,只需提供 POJO 和映射关系即可。
![](https://img2022.cnblogs.com/blog/2084611/202203/2084611-20220310205936045-929624510.png)
3)缓存机制比较
Hibernate 的二级缓存配置在 SessionFactory 生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置缓存。
MyBatis 的二级缓存配置在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且 Mybatis 可以在命名空间中共享相同的缓存配置和实例,通过 Cache-ref 来实现。
Hibernate 对查询对象有着良好的管理机制,用户无需关心 SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。而 MyBatis 在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免 Cache 的盲目使用。否则脏数据的出现会给系统的正常运行带来很大的隐患。
4)Hibernate 优势
- Hibernate 的 DAO 层开发比 MyBatis 简单,Mybatis 需要维护 SQL 和结果映射。
- Hibernate 对对象的维护和缓存要比 MyBatis 好,对增删改查的对象的维护要方便。
- Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。
- Hibernate 有更好的二级缓存机制,可以使用第三方缓存。MyBatis 本身提供的缓存机制不佳。
5)Mybatis优势
- MyBatis 可以进行更为细致的 SQL 优化,可以减少查询字段。
- MyBatis 容易掌握,而 Hibernate 门槛较高。
6)应用场景
MyBatis 适合需求多变的互联网项目,例如电商项目、金融类型、旅游类、售票类项目等。
Hibernate 适合需求明确、业务固定的项目,例如 OA 项目、ERP 项目和 CRM 项目等。
总结
总的来说,MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架,Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。
对于性能要求不太苛刻的系统,比如管理系统、ERP 等推荐使用 Hibernate,而对于性能要求高、响应快、灵活的系统则推荐使用 MyBatis。
MyBatis下载
在 MyBatis 的官方网站 http://mybatis.org,可以下载到最新版本的 MyBatis,本教程使用版本为 MyBatis 3.5.5。
如果您打不开网站或下载进度较慢,可以通过 https://github.com/mybatis/mybatis-3/releases 网址下载。
MyBatis核心对象
MyBatis 有三个基本要素:
- 核心接口和类
- MyBatis核心配置文件(mybatis-config.xml)
- SQL映射文件(mapper.xml)
下面首先介绍 MyBatis 的核心接口和类,如下所示。
每个 MyBatis 应用程序都以一个 SqlSessionFactory 对象的实例为核心。
首先获取 SqlSessionFactoryBuilder 对象,可以根据 XML 配置文件或者 Configuration 类的实例构建该对象。
然后获取 SqlSessionFactory 对象,该对象实例可以通过 SqlSessionFactoryBuilder 对象来获取。
有了 SqlSessionFactory 对象之后,就可以进而获取 SqlSession 实例。SqlSession 对象中完全包含以数据库为背景的所有执行 SQL 操作的方法,用该实例可以直接执行已映射的 SQL 语句。
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 会根据配置信息或者代码生成 SqlSessionFactory,并且提供了多个 build() 方法重载,如图。
![](http://c.biancheng.net/uploads/allimg/210708/1444224361-1.png)
通过源码分析,可以发现以上方法都是在调用同一签名方法,即:
build(Reader reader, String environment, Properties properties)
由于参数 environment 和 properties 都可以为 null,去除重复的方法,真正的重载方法其实只有如下三种:
- build(InputStream inputStream, String environment, Properties properties)
- build(Reader reader, String environment, Properties properties)
- build(Configuration config)
通过上述分析,发现配置信息可以以三种形式提供给 SqlSessionFactoryBuilder 的 build() 方法,分别是 InputStream(字节流)、Reader(字符流)、Configuration(类)。
由于字节流和字符流都属于读取配置文件的方式,所以就很容易想到构建一个 SqlSessionFactory 有两种方式,即:读取 XML 配置文件和编写代码。一般习惯为采取 XML 配置文件的方式来构造 SqlSessionFactory,这样一方面可以避免硬编码,另一方面方便日后配置人员修改,避免重复编译代码。
SqlSessionFactoryBuilder的生命周期和作用域
SqlSessionFactoryBuilder 的最大特点就是用过即丢。创建 SqlSessionFactory 对象之后,这个类就不存在了,因此 SqlSessionFactoryBuilder 的最佳范围就是存在于方法体内,也就是局部变量。
SqlSessionFactory
SqlSessionFactory 是工厂接口而不是现实类,他的任务就是创建 SqlSession。
所有的 MyBatis 应用都以 SqlSessionFactory 实例为中心,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 对象来获取。有了它以后,顾名思义,就可以通过 SqlSession 提供的 openSession() 方法来获取 SqlSession 实例。源码如下。
- public interface SqlSessionFactory {
- SqlSession openSession();
- SqlSession openSession(boolean autoCommit);
- SqlSession openSession(Connection connection);
- SqlSession openSession(TransactionIsolationLevel level);
- SqlSession openSession(ExecutorType execType);
- SqlSession openSession(ExecutorType execType, boolean autoCommit);
- SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
- SqlSession openSession(ExecutorType execType, Connection connection);
- Configuration getConfiguration();
- }
SqlSessionFactory的生命周期和作用域
SqlSessionFactory 对象一旦创建,就会在整个应用程序过程中始终存在。没有理由去销毁或再创建它,并且在应用程序运行中也不建议多次创建 SqlSessionFactory。因此 SqlSessionFactory 的最佳作用域是 Application,即随着应用程序的生命周期一直存在。这种“存在于整个应用运行期间,并且只存在一个对象实例”的模式就是所谓的单例模式(指在运行期间有且仅有一个实例)。
SqlSession
SqlSession 是用于执行持久化操作的对象,类似于 JDBC 中的 Connection。它提供了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例直接运行已映射的 SQL 语句。
- void clearCache();
- Configuration getConfiguration();
- void rollback(boolean force);
- void commit(boolean force);
- int delete(String statement, Object parameter);
- ...
SqlSession 的用途主要有两种。
- 获取映射器。让映射器通过命名空间和方法名称找到对应的 SQL,并发送给数据库,执行后返回结果。
- 直接通过“命名空间(namespace)+SQL id”的方式执行 SQL,不需要获取映射器。这是 iBatis 版本留下的方式。例如《第一个MyBatis程序》一节的示例就是这种方式执行的 SQL 语句。
SqlSession生命周期和作用域
SqlSession 对应一次数据库会话。由于数据库会话不是永久的,因此 SqlSession 的生命周期也不是永久的,每次访问数据库时都需要创建 SqlSession 对象。
MyBatis配置文件(mybatis-config.xml)
MyBatis 配置文件的结构如下。
- <?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
- <configuration><!-- 配置 -->
- <properties /><!-- 属性 -->
- <settings /><!-- 设置 -->
- <typeAliases /><!-- 类型命名 -->
- <typeHandlers /><!-- 类型处理器 -->
- <objectFactory /><!-- 对象工厂 -->
- <plugins /><!-- 插件 -->
- <environments><!-- 配置环境 -->
- <environment><!-- 环境变量 -->
- <transactionManager /><!-- 事务管理器 -->
- <dataSource /><!-- 数据源 -->
- </environment>
- </environments>
- <databaseIdProvider /><!-- 数据库厂商标识 -->
- <mappers /><!-- 映射器 -->
- </configuration>
mybatis-config.xml 文件中的元素节点是有一定顺序的,节点位置必须按以上位置排序,否则会编译错误。
下面介绍 XML 配置文件中的重要元素(标签)。
configuration 元素是整个 XML 配置文件的根节点,其角色就相当于是 MyBatis 的总管,MyBatis 所有的配置信息都会存放在它里面。
properties标签
properties 标签可以通过 resource 属性指定外部 properties 文件(database.properties),也可以通过 properties 子元素配置。
1. 指定文件
使用 properties 指定外部文件,代码如下。
- <properties resource="mybatisDemo/resources/database.properties"/>
database.properties 用于描述数据库连接的相关配置,例如数据库驱动、连接数据库的 url、数据库用户名、数据库密码等。
2. properties子元素配置
通过 properties 子元素 property 配置 username 和 password 变量,然后在 environments 节点中引用这些变量,代码如下。
- <properties>
- <property name="username" value="root"/>
- <property name="password" value="root"/>
- </properties>
在 environments 节点中引用 username 和 password 变量。
- <environments default="development">
- <environment id="development">
- <transactionManager type="JDBC"/>
- <dataSource type="POOLED">
- <property name="driver" value="${driver}"/>
- <property name="url" value="${url}"/>
- <property name="username" value="${username}"/>
- <property name="password" value="${password}"/>
- </dataSource>
- </environment>
- </environments>
也可以不使用 properties 标签,直接将属性值写在 value 中。
settings标签
settings 标签用于配置 MyBatis 的运行时行为,它能深刻的影响 MyBatis 的底层运行,一般不需要大量配置,大部分情况下使用其默认值即可。
settings 的配置项很多,但是真正用到的不会太多,我们把常用的配置项研究清楚就可以了。settings 配置项说明如下表所示(表中红色字体的配置项为常用配置项)。
配置项 | 作用 | 配置选项 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响所有映射器中配置缓存的全局开关 | true|false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 | true|false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true|false | 版本3.4.1 (不包含) 之前默认值为 true,之后为 false |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动) | true|false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 | true|false | true |
useGeneratedKeys | 允许JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby) | true|false | false |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射。 PARTIAL 表示只会自动映射,没有定义嵌套结果集和映射结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套) |
NONE、PARTIAL、FULL | PARTIAL |
autoMappingUnkno wnColumnBehavior | 指定自动映射当中未知列(或未知属性类型)时的行为。 默认是不处理,只有当日志级别达到 WARN 级别或者以下,才会显示相关日志,如果处理失败会抛出 SqlSessionException 异常 | NONE、WARNING、FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 是普通的执行器;REUSE 会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新 | SIMPLE、REUSE、BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数 | 任何正整数 | Not Set (null) |
defaultFetchSize | 设置数据库驱动程序默认返回的条数限制,此参数可以重新设置 | 任何正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。如果允许,设置 false | true|false | false |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false | true|false | true |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射 | true|false | false |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速联复嵌套査询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlScssion 的不同调用将不会共享数据 |
SESSION|STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER | NULL、VARCHAR、OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载 | — | equals、clone、hashCode、toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言 | — | org.apache.ibatis .script.ing.xmltags .XMLDynamicLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null | true|false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀 | 任何字符串 | Not set |
loglmpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 | SLF4J|LOG4J|LOG4J2|JDK_LOGGING |COMMONS_LOGGING |ST DOUT_LOGGING|NO_LOGGING |
Not set |
proxyFactory | 指定 MyBatis 创建具有延迟加栽能力的对象所用到的代理工具 | CGLIB|JAVASSIST | JAVASSIST (MyBatis 版本为 3.3 及以上的) |
vfsImpl | 指定 VFS 的实现类 | 提供 VFS 类的全限定名,如果存在多个,可以使用逗号分隔 | Not set |
useActualParamName | 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) | true|false | true |
下面给出一个全量的配置样例,如下所示。
- <settings>
- <setting name="cacheEnabled" value="true"/>
- <setting name="lazyLoadingEnabled" value="true"/>
- <setting name="multipleResultSetsEnabled" value="true"/>
- <setting name="useColumnLabel" value="true"/>
- <setting name="useGeneratedKeys" value="false"/>
- <setting name="autoMappingBehavior" value="PARTIAL"/>
- <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
- <setting name="defaultExecutorType" value="SIMPLE"/>
- <setting name="defaultStatementTimeout" value="25"/>
- <setting name="defaultFetchSize" value="100"/>
- <setting name="safeRowBoundsEnabled" value="false"/>
- <setting name="mapUnderscoreToCamelCase" value="false"/>
- <setting name="localCacheScope" value="SESSION"/>
- <setting name="jdbcTypeForNull" value="OTHER"/>
- <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
- </settings>
typeAliases标签
为了不在任何地方都指定类的全限定名,我们可以使用 typeAliases 标签定义一个别名。
例如,在 net.bianchengbang.po 包中有一个 Student 类,则该类的全限定名称为 net.bianchengbang.po.Student。使用 typeAliases 标签定义别名,这样就不用每次都书写类的全限定名称了,代码如下。
- <typeAliases>
- <typeAlias alias = "Student" type = "net.bianchengbang.po.Student"/>
- </typeAliases>
如果需要对同一个包下的多个类定义别名,则可以定义为:
- <typeAliases>
- <package name="net.biancheng.po"/>
- </typeAliases>
这样 MyBatis 将扫描 net.biancheng.po 包里面的类,将其第一个字母变为小写作为其别名,例如 Student 别名为 student,User 别名为 user。
typeHandlers标签
typeHandlers 主要将获取的值合理地转化为 Java 类型。在 typeHandler 中,分为 jdbcType 和 javaType,其中 jdbcType 用于定义数据库类型,而 javaType 用于定义 Java 类型,typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换。
MyBatis 支持自定义处理类型,在自定义处理类型时,需要实现 org.apache.ibatis.type.TypeHandler 接口或继承 org.apache.ibatis.type.BaseTypeHandle 类。详细可参考官网:http://www.mybatis.org/mybatis-3/zh/configuration.html#typeHandlers
environments标签
在 environments 标签中,可以配置 MyBatis 的多套运行环境,将 SQL 映射到多个不同的数据库上。
environment 是 environments 的子标签,用来配置 MyBatis 的一套运行环境,需指定运行环境 ID、事务管理、数据源配置等相关信息。
我们可以通过配置多个 environment 标签来连接多个数据库,需要注意的是必须指定其中一个为默认运行环境(通过default指定)。
environment 标签提供了两个子标签,即 transactionManager 和 dataSource。
transactionManager标签
MyBatis 支持两个事务管理器,即 JDBC 和 MANAGED。
如果使用 JDBC 类型的事务管理器,则应用程序服务器负责事务管理操作,例如提交、回滚等。如果使用 MANAGED 类型的事务管理器,则应用程序服务器负责管理连接生命周期。
dataSource标签
用于配置数据库的连接属性,例如要连接的数据库的驱动程序名称、URL、用户名和密码等。
dataSource 中的 type 属性用于指定数据源类型,有以下 3 种类型。
1)UNPOOLED
UNPOOLED 没有数据库连接池,效率低下。MyBatis 需要打开和关闭每个数据库操作的连接,它有点慢,通常应用于简单的应用程序。
2)POOLED
对于 POOLED 数据源类型,MyBatis 将维护一个数据库连接池。并且对于每个数据库的操作,MyBatis 都会使用连接池中的连接,并在操作完成后将它们返回到池中。减少了创建新连接所需的初始连接和身份验证时间。
3)JNDI
对于 JNDI 的数据源类型,MyBatis 将从 JNDI 数据源中获取连接。
dataSource 标签示例代码如下:
- <dataSource type="POOLED">
- <!-- MySQL数据库驱动 -->
- <property name="driver" value="com.mysql.jdbc.Driver" />
- <!-- 连接数据库的URL -->
- <property name="url"
- value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8" />
- <property name="username" value="root" />
- <property name="password" value="root" />
- </dataSource>
mappers标签
mappers 标签用于指定 MyBatis SQL 映射文件的路径。
mapper 是 mappers 的子标签,mapper 中的 resource 属性用于指定 SQL 映射文件的路径(类资源路径)
例如,SQL 映射文件的名称是 Student.xml,它位于名为 net.biancheng.mapper 的包中,那么您可以这样配置:
- <mappers>
- <mapper resource="net/biancheng/mapper/Student.xml"/>
- </mappers>
XML实现映射器
映射器有以下两种实现方式。
- 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。
- 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。
XML实现映射器
XML 定义映射器分为两个部分:接口和XML。下面先定义接口 WebsiteMapper(本节基于《第一个MyBatis程序》中的示例实现)。
- package net.biancheng.mapper;
- import java.util.List;
- import net.biancheng.po.Website;
- public interface WebsiteMapper {
- public List<Website> selectAllWebsite();
- }
WebsiteMapper.xml 代码如下。
- <?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="net.biancheng.mapper.WebsiteMapper">
- <!-- 查询所有网站信息 -->
- <select id="selectAllWebsite"
- resultType="net.biancheng.po.Website">
- select * from website
- </select>
- </mapper>
下面对上述 XML 文件进行讲解。
- namespace 用来定义命名空间,该命名空间和定义接口的全限定名一致。
- <select> 元素表明这是一条查询语句,属性 id 用来标识这条 SQL。resultType 表示返回的是一个 Website 类型的值。
在 MyBatis 配置文件中添加以下代码。
- <mapper resource="net/biancheng/mapper/WebsiteMapper.xml" />
该语句用来引入 XML 文件,MyBatis 会读取 WebsiteMapper.xml 文件,生成映射器。
注解实现映射器
使用注解的方式实现映射器,只需要在接口中使用 Java 注解,注入 SQL 即可。如下所示。
- package net.biancheng.mapper;
- import java.util.List;
- import org.apache.ibatis.annotations.Select;
- import net.biancheng.po.Website;
- public interface WebsiteMapper2 {
- @Select(value = "select * from website")
- public List<Website> selectAllWebsite();
- }
这里我们使用了 @Select 注解,并且注入了和 XML 中相同的 select 语句。
如果使用注解和 XML 文件两种方式同时定义,那么 XML 方式将覆盖掉注解方式。
虽然这里注解的方式看起来比 XML 简单,但是现实中我们遇到的 SQL 会比该例子复杂得多。如果 SQL 语句中有多个表的关联、多个查询条件、级联、条件分支等,显然这条 SQL 就会复杂的多,所以并不建议读者使用这种方式。比如下面这条 SQL。
MyBatis 映射器的主要元素
下面介绍在映射器中可以定义哪些元素,以及它们的作用。
元素名称 | 描述 | 备注 |
---|---|---|
mapper | 映射文件的根节点,只有 namescape 一个属性 | namescape 作用如下:
|
select | 查询语句,最常用、最复杂的元素之一 | 可以自定义参数,返回结果集等 |
insert | 插入语句 | 执行后返回一个整数,代表插入的条数 |
update | 更新语句 | 执行后返回一个整数,代表更新的条数 |
delete | 删除语句 | 执行后返回一个整数,代表删除的条数 |
parameterMap | 定义参数映射关系 | 即将被删除的元素,不建议使用 |
sql | 允许定义一部分的 SQL,然后在各个地方引用它 | 例如,一张表列名,我们可以一次定义,在多个 SQL 语句中使用 |
resultMap | 用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素 | 提供映射规则 |
cache | 配置给定命名空间的缓存 | - |
cache-ref | 其它命名空间缓存配置的引用 | - |
后面文章中会详细介绍以上元素。
拓展
关于 MyBatis 的 SQL 映射文件中的 mapper 元素的 namescape 属性有如下要求。
- namescape 的命名必须跟某个 DAO 接口同名,同属于 DAO 层,因此代码结构上,映射文件与该接口应放置在同一 package 下(如 net.biancheng.dao.website),并且习惯上是以 Mapper 结尾(如 WebsiteMapper.java、WebsiteMapper.xml)。
- 不同的 mapper 文件中子元素的 id 可以相同,MyBatis 通过 namescape 和子元素的 id 联合区分。接口中的方法与映射文件中的 SQL 语句 id 应一 一对应。
MyBatis执行SQL的两种方式
MyBatis 有两种执行 SQL 语句的方式,如下:
- 通过 SqlSession 发送 SQL
- 通过 SqlSession 获取 Mapper 接口,通过 Mapper 接口发送 SQL
区别
上面分别讲解了 MyBatis 两种发送 SQL 的方式,一种用 SqlSession 直接发送,另外一种通过 SqlSession 获取 Mapper 接口再发送。笔者建议采用 Mapper 接口发送 SQL 的方式,理由如下:
- 使用 Mapper 接口编程可以消除 SqlSession 带来的功能性代码,提高可读性,而 SqlSession 发送 SQL,需要一个 SQL id 去匹配 SQL,比较晦涩难懂。
- 使用 Mapper 接口,类似 websiteMapper.getWebsite(1) 则是完全面向对象的语言,更能体现业务的逻辑。
- 使用 websiteMapper.getWebsite(1) 方式,IDE 会提示错误和校验,而使用 sqlSession.selectOne("getWebsite",1L) 语法,只有在运行中才能知道是否会产生错误。
MyBatis select标签
select标签常用属性
下面介绍 select 标签中常用的属性。
属性名称 | 描 述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+id不唯一,那么 MyBatis 抛出异常 |
parameterType | 表示传入 SQL 语句传入参数类型的全限定名或别名。它是一个可选属性,MyBatis 能推断出具体传入语句的参数 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
resultType | SQL 语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一 | - |
resultMap | 它是映射集的引用,与 <resultMap> 元素一起使用,返回时可以使用 resultType 或 resultMap 之一 | 是 MyBatis 最复杂的元素,可以配置映射规则、级联、typeHandler 等 |
flushCache | 用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存 | 默认值为 false,如果设置为 true,则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存 |
useCache | 启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中 | - |
timeout | 用于设置超时参数,单位是秒(s),超时将抛出异常 | - |
fetchSize | 获取记录的总条数设定 | 默认值是数据库厂商提供的 JDBC 驱动所设置的条数 |
statementType | 告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、CALLABLE(CallableStatement) | - |
resultSetType | 这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新) | - |
传递多个参数
现在需要根据 id 和 name 来模糊查询网站信息,显然这涉及到了两个参数。给映射器传递多个参数分为以下三种方法。
- 使用Map传递参数
- 使用注解传递参数
- 使用JavaBean传递参数
下面分别讲解这 3 种方法的具体实现。
1. 使用Map传递参数
使用 MyBatis 提供的 Map 接口作为参数实现,如下所示。
- <!-- 根据name和url模糊查询网站信息 -->
- <select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
- SELECT id,NAME,url FROM website
- WHERE name LIKE CONCAT ('%',#{name},'%')
- AND url LIKE CONCAT ('%',#{url},'%')
- </select>
在 WebsiteMapper 接口中,方法如下。
- public List<Website> selectWebsiteByMap(Map<String, String> params);
测试代码如下。
- Map<String,String> paramsMap = new HashMap<String,String>();
- paramsMap.put("name","编程");
- paramsMap.put("url","biancheng");
- websiteMapper.selectWebsiteByMap(paramsMap);
使用 Map 传递参数虽然简单易用,但是由于这样设置参数需要键值对应,业务关联性不强,开发人员需要深入到程序中看代码,造成可读性下降。
2. 使用注解传递参数
使用 MyBatis 的注解 @Param() 传递参数,如下所示。
- <!-- 根据name和url模糊查询网站信息 -->
- <select id="selectWebsiteByAn" resultType="net.biancheng.po.Website">
- SELECT id,NAME,url FROM website
- WHERE name LIKE CONCAT ('%',#{name},'%')
- AND url LIKE CONCAT ('%',#{url},'%')
- </select>
WebsiteMapper 接口中方法如下。
- public List<Website> selectWebsiteByAn(@Param("name") String name, @Param("url") String url);
当我们把参数传递给后台时,MyBatis 通过 @Param 提供的名称就会知道 #{name} 代表 name 参数,提高了参数可读性。但是如果这条 SQL 拥有 10 个参数的查询,就会造成可读性下降,增强了代码复杂性。
3. 使用JavaBean传递参数
在参数过多的情况下,MyBatis 允许组织一个 JavaBean,通过简单的 setter 和 getter 方法设置参数,提高可读性。如下所示。
- <!-- 根据name和url模糊查询网站信息 -->
- <select id="selectWebsiteByAn" resultType="net.biancheng.po.Website">
- SELECT id,NAME,url FROM website
- WHERE name LIKE CONCAT ('%',#{name},'%')
- AND url LIKE CONCAT ('%',#{url},'%')
- </select>
WebsiteMapper 接口中方法如下。
- public List<Website> selectWebsiteByAn(Website website);
这就是通过 JavaBean 传递多个参数的方式。
区别
以上 3 种方式的区别如下。
- 使用 Map 传递参数会导致业务可读性的丧失,继而导致后续扩展和维护的困难,所以在实际应用中我们应该果断废弃该方式。
- 使用 @Param 注解传递参数会受到参数个数的影响。当 n≤5 时,它是最佳的传参方式,因为它更加直观;当 n>5 时,多个参数将给调用带来困难。
- 当参数个数大于 5 个时,建议使用 JavaBean 方式。
MyBatis insert标签
insert 标签常用属性
insert 标签中常用的属性如下表。
属性名称 | 描述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常 |
parameterType | 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
keyProperty | 该属性的作用是将插入操作的返回值赋给 PO 类的某个属性,通常为主键对应的属性。如果是联合主键,可以将多个值用逗号隔开。 | - |
useGeneratedKe | 该属性用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。 | 该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。 |
flushCache | 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 | - |
timeout | 该属性用于设置执行该操作的最大时限,如果超时,就抛异常。 | - |
databaseId | 取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过 <if test="_databaseId = 'oracle'"> 来为特定数据库指定不同的 sql 语句。 | MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 |
keyColumn | 该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。 | - |
注意:insert 标签中没有 resultType 属性,只有查询操作才需要对返回结果类型进行相应的指定。
传递多个参数
在上面的示例中,我们在插入语句中只使用了一个 String 类型的参数,而在实际的开发过程中,我们的插入语句往往需要使用多个参数,Mybatis 为我们提供以下 3 种方式,来实现给映射器传递多个参数:
- 使用 Map 传递参数
- 使用注解传递参数
- 使用 JavaBean 传递参数
主键(自动递增)回填
我们知道,MySQL、SQL Server 等数据库表可以采用自动递增的字段作为其主键,当向这样的数据库表插入数据时,即使不指定自增主键的值,数据库也会根据自增规则自动生成主键并插入到表中。
一些特殊情况下,我们可能需要将这个刚刚生成的主键回填到请求对象(原本不包含主键信息的请求对象)中,供其他业务使用。此时,我们就可以通过在 insert 标签中添加 keyProperty 和 useGeneratedKeys 属性,来实现该功能。
下面我们通过一个示例,来演示主键(自动递增)回填功能。
1. 为 WebsiteMapper.xml 中 id 为 addWebsite 的 insert 标签添加 keyProperty 和 useGeneratedKeys 属性,具体代码如下:
- <!--添加一个网站信息,成功后将主键值返回填给id(po的属性)-->
- <insert id="addWebsite" parameterType="net.biancheng.po.Website" keyProperty="id" useGeneratedKeys="true">
- insert into Website (name,url) values(#{name},#{url})
- </insert>
MyBatis update标签
update 标签常用属性
update 标签常用属性如下表。
属性名称 | 描述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常 |
parameterType | 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
flushCache | 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 | - |
timeout | 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 | - |
statementType | 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 | - |
注意:update 标签中没有 resultType 属性,只有查询操作才需要对返回结果类型进行相应的指定。
传递多个参数
在上面的示例中,我们在更新语句中只使用了一个 String 类型的参数,而在实际的开发过程中,大多数时候,我们的更新语句都需要使用多个参数,Mybatis 为我们提供以下 3 种方式,来实现给映射器传递多个参数:
- 使用 Map 传递参数
- 使用注解传递参数
- 使用 JavaBean 传递参数
MyBatis delete标签
delete 标签常用属性
delete 标签常用属性如下表。
属性名称 | 描述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常 |
parameterType | 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
flushCache | 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 | - |
timeout | 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 | - |
statementType | 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 | - |
注意:delete 标签中没有 resultType 属性,只有查询操作才需要对返回结果类型进行相应的指定。
传递多个参数
在上面的示例中,我们在 delete 语句中只使用了一个 String 类型的参数,而在实际的开发过程中,我们的 delete 语句往往需要使用多个参数对记录进行筛选,Mybatis 为我们提供以下 3 种方式,来实现给映射器传递多个参数:
- 使用 Map 传递参数
- 使用注解传递参数
- 使用 JavaBean 传递参数
MyBatis resultMap元素
resultMap元素的构成
resultMap 元素还可以包含以下子元素,代码如下。
- <resultMap id="" type="">
- <constructor><!-- 类再实例化时用来注入结果到构造方法 -->
- <idArg/><!-- ID参数,结果为ID -->
- <arg/><!-- 注入到构造方法的一个普通结果 -->
- </constructor>
- <id/><!-- 用于表示哪个列是主键 -->
- <result/><!-- 注入到字段或JavaBean属性的普通结果 -->
- <association property=""/><!-- 用于一对一关联 -->
- <collection property=""/><!-- 用于一对多、多对多关联 -->
- <discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
- <case value=""/><!-- 基于某些值的结果映射 -->
- </discriminator>
- </resultMap>
其中:
- <resultMap> 元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。
- 子元素 <constructor> 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。
- 子元素 <id> 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
- 子元素 <result> 用于表示 POJO 和 SQL 列名的映射关系。
- 子元素 <association>、<collection> 和 <discriminator> 用在级联的情况下。
id 和 result 元素都有以下属性。
元素 | 说明 |
---|---|
property | 映射到列结果的字段或属性。如果 POJO 的属性和 SQL 列名(column元素)是相同的,那么 MyBatis 就会映射到 POJO 上 |
column | 对应 SQL 列 |
javaType | 配置 Java 类型。可以是特定的类完全限定名或 MyBatis 上下文的别名 |
jdbcType | 配置数据库类型。这是 JDBC 类型,MyBatis 已经为我们做了限定,基本支持所有常用数据库类型 |
typeHandler | 类型处理器。允许你用特定的处理器来覆盖 MyBatis 默认的处理器。需要指定 jdbcType 和 javaType 相互转化的规则 |
一条 SQL 查询语句执行后会返回结果集,结果集有两种存储方式,即使用 Map 存储和使用 POJO 存储。
resultType和resultMap的区别
MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。
需要注意的是,resultMap 和 resultType 不能同时使用。
MyBatis注解(3种类型)
1. SQL 语句映射
1)@Insert:实现新增功能
- @Insert("insert into user(id,name) values(#{id},#{name})")
- public int insert(User user);
2)@Select:实现查询功能
- @Select("Select * from user")
- @Results({
- @Result(id = true, column = "id", property = "id"),
- @Result(column = "name", property = "name"),
- @Result(column = "sex", property = "sex"),
- @Result(column = "age", property = "age")
- })
- List<User> queryAllUser();
3)@SelectKey:插入后,获取id的值
以 MySQL 为例,MySQL 在插入一条数据后,使用 select last_insert_id() 可以获取到自增 id 的值。
- @Insert("insert into user(id,name) values(#{id},#{name})")
- @SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int,before = false)
- public int insert(User user);
@SelectKey 各个属性含义如下。
- statement:表示要运行的 SQL 语句;
- keyProperty:可选项,表示将查询结果赋值给代码中的哪个对象;
- keyColumn:可选项,表示将查询结果赋值给数据表中的哪一列;
- resultType:指定 SQL 语句的返回值;
- before:默认值为 true,在执行插入语句之前,执行 select last_insert_id()。值为 flase,则在执行插入语句之后,执行 select last_insert_id()。
4)@Insert:实现插入功能
- @Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age}")
- int saveUser(User user);
5)@Update:实现更新功能
- @Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
- void updateUserById(User user);
6)@Delete:实现删除功能
- @Delete("delete from user where id =#{id}")
- void deleteById(Integer id);
7)@Param:映射多个参数
@Param 用于在 Mapper 接口中映射多个参数。
- int saveUser(@Param(value="user") User user,@Param("name") String name,@Param("age") Int age);
@Param 中的 value 属性可省略,用于指定参数的别名。
2. 结果集映射
@Result、@Results、@ResultMap 是结果集映射的三大注解。
声明结果集映射关系代码:
- @Select({"select id, name, class_id from student"})
- @Results(id="studentMap", value={
- @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
- @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
- @Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
- })
- List<Student> selectAll();
下面为 @Results 各个属性的含义。
- id:表示当前结果集声明的唯一标识;
- value:表示结果集映射关系;
- @Result:代表一个字段的映射关系。其中,column 指定数据库字段的名称,property 指定实体类属性的名称,jdbcType 数据库字段类型,id 为 true 表示主键,默认 false。
可使用 @ResultMap 来引用映射结果集,其中 value 可省略。
- @Select({"select id, name, class_id from student where id = #{id}"})
- @ResultMap(value="studentMap")
- Student selectById(Integer id);
这样不需要每次声明结果集映射时都复制冗余代码,简化开发,提高了代码的复用性。
3. 关系映射
1)@one:用于一对一关系映射
- @Select("select * from student")
- @Results({
- @Result(id=true,property="id",column="id"),
- @Result(property="name",column="name"),
- @Result(property="age",column="age"),
- @Result(property="address",column="address_id",one=@One(select="net.biancheng.mapper.AddressMapper.getAddress"))
- })
- public List<Student> getAllStudents();
2)@many:用于一对多关系映射
- @Select("select * from t_class where id=#{id}")
- @Results({
- @Result(id=true,column="id",property="id"),
- @Result(column="class_name",property="className"),
- @Result(property="students", column="id", many=@Many(select="net.biancheng.mapper.StudentMapper.getStudentsByClassId"))
- })
- public Class getClass(int id);
MyBatis关联(级联)查询
MyBatis一对一关联查询
在 MyBatis 中,通过 <resultMap> 元素的子元素 <association> 处理一对一级联关系。示例代码如下。
- <association property="studentCard" column="cardId"
- javaType="net.biancheng.po.StudentCard"
- select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />
在 <association> 元素中通常使用以下属性。
- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
一对一关联查询可采用以下两种方式:
- 单步查询,通过关联查询实现
- 分步查询,通过两次或多次查询,为一对一关系的实体 Bean 赋值
StudentMapper.xml 代码如下。
<mapper namespace="net.biancheng.mapper.StudentMapper">
<!-- 一对一根据id查询学生信息:级联查询的第一种方法(嵌套查询,执行两个SQL语句) -->
<resultMap type="net.biancheng.po.Student" id="cardAndStu1">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<!-- 一对一级联查询 -->
<association property="studentCard" column="cardId"
javaType="net.biancheng.po.StudentCard"
select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />
</resultMap>
<select id="selectStuById1" parameterType="Integer"
resultMap="cardAndStu1">
select * from student where id=#{id}
</select>
</mapper>
MyBatis一对多关联查询
在 MyBatis 中,通过 <resultMap> 元素的子元素 <collection> 处理一对多级联关系,collection 可以将关联查询的多条记录映射到一个 list 集合属性中。示例代码如下
- <collection property="orderList"
- ofType="net.biancheng.po.Order" column="id"
- select="net.biancheng.mapper.OrderMapper.selectOrderById" />
在 <collection> 元素中通常使用以下属性。
- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
一对多关联查询可采用以下两种方式:
- 分步查询,通过两次或多次查询,为一对多关系的实体 Bean 赋值
- 单步查询,通过关联查询实现
UserMapper.xml 中相应的映射 SQL 语句如下。
<!-- 一对多 根据id查询用户及其关联的订单信息:级联查询的第一种方法(分步查询) -->
<resultMap type="net.biancheng.po.User" id="userAndOrder1">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="pwd" column="pwd" />
<!-- 一对多级联查询,ofType表示集合中的元素类型,将id传递给selectOrderById -->
<collection property="orderList"
ofType="net.biancheng.po.Order" column="id"
select="net.biancheng.mapper.OrderMapper.selectOrderById" />
</resultMap>
<select id="selectUserOrderById1" parameterType="Integer"
resultMap="userAndOrder1">
select * from user where id=#{id}
</select>
MyBatis动态SQL
MyBatis 的动态 SQL 包括以下几种元素,如下表所示。
元素 | 作用 | 备注 |
---|---|---|
if | 判断语句 | 单条件分支判断 |
choose(when、otherwise) | 相当于 Java 中的 switch case 语句 | 多条件分支判断 |
trim、where | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
bind | 辅助元素 | 拼接参数 |
MyBatis if标签:条件判断
最常见的场景是在 if 语句中包含 where 子句,例如。
- <select id="selectAllWebsite" resultMap="myResult">
- select id,name,url from website
- <if test="name != null">
- where name like #{name}
- </if>
- </select>
MyBatis choose、when和otherwise标签
<mapper namespace="net.biancheng.mapper.WebsiteMapper">
<select id="selectWebsite"
parameterType="net.biancheng.po.Website"
resultType="net.biancheng.po.Website">
SELECT id,name,url,age,country
FROM website WHERE 1=1
<choose>
<when test="name != null and name !=''">
AND name LIKE CONCAT('%',#{name},'%')
</when>
<when test="url != null and url !=''">
AND url LIKE CONCAT('%',#{url},'%')
</when>
<otherwise>
AND age is not null
</otherwise>
</choose>
</select>
</mapper>
MyBatis where标签
<select id="selectWebsite" resultType="net.biancheng.po.Website">
select id,name,url from website
<where>
<if test="name != null">
AND name like #{name}
</if>
<if test="url!= null">
AND url like #{url}
</if>
</where>
</select>
MyBatis set标签
- <!--使用set元素动态修改一个网站记录 -->
- <update id="updateWebsite"
- parameterType="net.biancheng.po.Website">
- UPDATE website
- <set>
- <if test="name!=null">name=#{name}</if>
- <if test="url!=null">url=#{url}</if>
- </set>
- WHERE id=#{id}
- </update>
MyBatis foreach标签
Mybatis foreach 标签用于循环语句,它很好的支持了数据和 List、set 接口的集合,并对此提供遍历的功能。语法格式如下。
- <foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
- 参数值
- </foreach>
foreach 标签主要有以下属性,说明如下。
- item:表示集合中每一个元素进行迭代时的别名。
- index:指定一个名字,表示在迭代过程中每次迭代到的位置。
- open:表示该语句以什么开始(既然是 in 条件语句,所以必然以
(
开始)。 - separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以
,
作为分隔符)。 - close:表示该语句以什么结束(既然是 in 条件语句,所以必然以
)
开始)。
使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
- 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
- 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
- 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。
- <select id="selectWebsite"
- parameterType="net.biancheng.po.Website"
- resultType="net.biancheng.po.Website">
- SELECT id,name,url,age,country
- FROM website WHERE age in
- <foreach item="age" index="index" collection="list" open="("
- separator="," close=")">
- #{age}
- </foreach>
- </select>
MyBatis bind标签
- <select id="selectWebsite" resultType="net.biancheng.po.Website">
- <bind name="pattern" value="'%'+_parameter+'%'" />
- SELECT id,name,url,age,country
- FROM website
- WHERE name like #{pattern}
- </select>
MyBatis trim标签
trim 中属性说明如下。
- <trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
- SQL语句
- </trim>
属性 | 描述 |
---|---|
prefix | 给SQL语句拼接的前缀,为 trim 包含的内容加上前缀 |
suffix | 给SQL语句拼接的后缀,为 trim 包含的内容加上后缀 |
prefixOverrides | 去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。 |
suffixOverrides | 去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定。 |
示例
- <select id="selectWebsite" resultType="net.biancheng.po.Website">
- SELECT id,name,url,age,country
- FROM website
- <trim prefix="where" prefixOverrides="and">
- <if test="name != null and name !=''">
- AND name LIKE CONCAT ('%',#{name},'%')
- </if>
- <if test="url!= null">
- AND url like concat ('%',#{url},'%')
- </if>
- </trim>
- </select>
MyBatis分页功能
修改 WebsiteMapper.xml 的查询语句,增加 limit 关键字,SQL 映射代码如下。
<select id="selectWebsite" resultType="net.biancheng.po.Website">
SELECT id,name,url,age,country
FROM website
<trim prefix="where" prefixOverrides="and">
<if test="site.name != null and site.name !=''">
AND name LIKE CONCAT ('%',#{site.name},'%')
</if>
<if test="site.url!= null and site.url !=''">
AND url LIKE CONCAT ('%',#{site.url},'%')
</if>
ORDER BY id limit #{from},#{pageSize}
</trim>
</select>
MyBatis缓存(一级缓存和二级缓存)
一级缓存
一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
二级缓存
二级缓存是全局缓存,作用域超出 session 范围之外,可以被所有 SqlSession 共享。一级缓存缓存的是 SQL 语句,二级缓存缓存的是结果对象。
二级缓存的配置
1)MyBatis 的全局缓存配置需要在 mybatis-config.xml 的 settings 元素中设置,代码如下。- <settings>
- <setting name="cacheEnabled" value="true" />
- </settings>
- <mapper namescape="net.biancheng.WebsiteMapper">
- <!-- cache配置 -->
- <cache
- eviction="FIFO"
- flushInterval="60000"
- size="512"
- readOnly="true" />
- ...
- </mapper>
属性 | 说明 |
---|---|
eviction | 代表的是缓存回收策略,目前 MyBatis 提供以下策略。
|
flushInterval | 刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存。 |
size | 引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象。 |
readOnly | 只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存。 |
3)在 mapper 文件配置支持 cache 后,如果需要对个别查询进行调整,可以单独设置 cache,代码如下。
- <select id="getWebsiteList" resultType="net.biancheng.po.Website" usecache="true">
- ...
- </select>
MyBatis逆向工程
1. 下载jar包
jar包下载链接:https://github.com/mybatis/generator/releases。本节下载版本为 mybatis-generator-core-1.4.0。
maven 项目在 pom.xml 文件中添加以下代码。
- <dependency>
- <groupId>org.mybatis.generator</groupId>
- <artifactId>mybatis-generator-core</artifactId>
- <version>1.4.0</version>
- </dependency>