MyBatis 入门教程之八
大纲
- MyBatis 入门教程之一
- MyBatis 入门教程之二
- MyBatis 入门教程之三
- MyBatis 入门教程之四
- MyBatis 入门教程之五
- MyBatis 入门教程之六
- MyBatis 入门教程之七
- MyBatis 入门教程之八
MyBatis 实用场景
MyBatis 批量操作
调用不带参数的 openSession()
方法时,创建的 SqlSession
对象具有如下特性:
- 会开启一个事务(不会自动提交)
- 数据库连接对象会从由环境配置的数据源实例得到
- 事务隔离级别将会使用驱动或数据源的默认配置
- 预处理语句不会被复用,也不会批量处理更新
值得一提的是,openSession()
方法的 ExecutorType
类型的参数是枚举类型,取值如下:
SIMPLE
:这个执行器类型不做特殊的事情(默认执行器),它会为每个 SQL 语句的执行创建一个新的预处理语句。REUSE
:这个执行器类型会复用预处理语句。BATCH
:这个执行器会批量执行所有更新语句。
批量操作介绍
批量操作就是使用 MyBatis 提供的 BATCH
类型的 Executor
进行的,它的底层是通过 JDBC 暂存 SQL 语句的方式来实现,也就是保存 SQL 语句到一定数量后再发给数据库执行一次。MyBatis 与 Spring 整合时,推荐额外配置一个可以专门用来执行批量操作的 SqlSession
,配置示例如下。当需要使用批量操作的时候,可以在 Service
层注入可批量操作的 SqlSession
,然后通过它获取 Mapper
映射器来操作数据库。
1 | <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> |
特别注意
- 当 ExecutorType 指定为
BATCH
后,执行增删改操作不会再返回正确的受影响的记录数。 - 批量操作是在
session.commit()
执行以后才发送 SQL 语句给数据库执行的。若想让其提前执行,以方便后续的查询操作可以获取到数据,则可以调用sqlSession.flushStatements()
方法,让其直接冲刷到数据库进行执行。
批量操作使用案例
本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-22
。
- Mapper 接口
1 | public interface EmployeeMapper { |
- SQL 映射文件
1 | <mapper namespace="com.clay.mybatis.dao.EmployeeMapper"> |
- 批量操作
1 | public class MyBatisApplication { |
- 执行结果
1 | 21:25:28,897 DEBUG JdbcTransaction:137 - Opening JDBC Connection |
观察上面的执行结果,可以发现使用 MyBatis 的批量操作插入多条数据时,不会发送多条 SQL 语句到数据库,这大大提高了业务逻辑的执行效率。
PageHelper 分页插件
PageHelper 是 MyBatis 第三方的分页插件,支持任何复杂的单表、多表分页查询,部分特殊情况请看重要提示。
PageHelper 官方文档
PageHelper 使用案例
本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-21
。
- 引入依赖
1 | <dependency> |
- 注册插件
1 | <configuration> |
- Mapper 接口
1 | public interface EmployeeMapper { |
- SQL 映射文件
提示
在编写 SQL 语句的时候,不需要手动指定任何分页参数。
1 | <mapper namespace="com.clay.mybatis.dao.EmployeeMapper"> |
- 业务逻辑,PageHelper 更多 API 的使用示例可以看 这里
1 | public class MyBatisApplication { |
- 执行结果
1 | 23:16:35,705 DEBUG getEmps_COUNT:137 - ==> Preparing: SELECT count(0) FROM t_employee |
MyBatis 调用存储过程
存储过程(Stored Procedure)是一种存储在数据库中的复杂程序,以便外部程序调用的一种数据库对象。存储过程是为了完成特定功能的 SQL 语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名称并给定参数(需要时)来调用执行。存储过程的设计思想很简单,本质就是数据库 SQL 语言层面的代码封装与重用。
存储过程的优缺点
存储过程的优点:
- 极大地提高 SQL 语言的功能和灵活性
- 可保证数据的安全性和完整性
- 通过存储过程可以使相关的动作在一起发生,从而可以维护数据库的完整性
- 通过存储过程可以使没有权限的用户在控制之下间接地存取数据库,从而保证数据的安全
- 极大地改善 SQL 语句的性能
- 在运行存储过程前,数据库已对其进行了语法和句法分析,并给出优化执行方案,这种已经编译好的过程可极大地改善 SQL 语句的性能。由于执行 SQL 语句的大部分解析工作已经完成,所以存储过程能以极快的速度执行
- 可以降低网络的通信量
- 客户端调用存储过程时,只需要传存储过程名和相关参数即可,与传输 SQL 语句相比自然数据量少了很多
存储过程的缺点:
- 开发阶段难以调试
- 难以移植,存储过程往往定制化于特定的数据库上,当切换到其他厂商的数据库系统时,往往需要重写原有的存储过程
- 如果在一个系统中大量的使用存储过程,到程序交付使用的时候,随着用户需求的增加会导致数据结构的变化,存储过程也要同步更新。最后如果用户想维护该系统可以说是很难的,而且代价是空前的,维护起来更麻烦
MySQL 存储过程的语法
- 声明语句结束符
1 | delimiter $$ |
- 声明存储过程
1 | create procedure demo_in_parameter(in p_in int) |
- 存储过程的开始和结束符号
1 | begin .... end |
- 变量定义
1 | declare l_int int unsigned default 4000000; |
- 变量赋值
1 | set @p_in=1 |
- 调用存储过程
1 | call demo_in_parameter(); |
- 删除存储过程
1 | drop procedure demo_in_parameter; |
- 创建存储过程(完整的)
1 | delimiter $$ |
存储过程调用案例
本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-23
。
提示
- 若需要调用存储过程,可以使用
<select>
标签,并设置标签的statementType
属性为CALLABLE
即可 <select>
标签体中存储过程的调用语法:{ call procedure_name(#{param1_info}, #{param2_info}) }
MySQL 存储过程调用案例一
在下面的案例中,演示了如何在 MySQL 数据库中使用存储过程来根据员工 ID
查询员工的详细信息。
- JavaBean 类
1 | public class Employee { |
- Mapper 接口
1 | public interface EmployeeMapper { |
- 创建 MySQL 存储过程
1 | delimiter $$ |
- SQL 映射文件
1 | <mapper namespace="com.clay.mybatis.dao.EmployeeMapper"> |
- 业务逻辑
1 | public class MyBatisApplication { |
- 执行结果
1 | 22:58:51,817 DEBUG getEmpById:137 - ==> Preparing: { call query_single_employee(?) } |
Oracle 存储过程调用案例二
MyBatis 对存储过程的游标提供了一个 JdbcType=CURSOR
的支持,可以智能地通过游标读取数据集,并将数据集自动映射到声明的结果集中。在下面的案例中,简单演示了如何在 Oracle 数据库中使用存储过程来实现分页查询的逻辑(这里只是简单演示,并未完全真正实现分页查询的逻辑)。
- JavaBean 类
1 | public class Employee { |
1 | public class PageEmp { |
- Mapper 接口
1 | public interface EmployeeMapper { |
- 创建 Oracle 存储过程
1 | create or replace procedure |
- SQL 映射文件
1 | <mapper namespace="com.clay.mybatis.dao.EmployeeMapper"> |
- MyBatis 全局配置文件
1 | <!-- 数据库厂商标识 --> |
- 业务逻辑
1 | public class MyBatisApplication { |
MyBatis 枚举类型处理器
默认的枚举处理器
MyBatis 在处理枚举类型对象的时候,默认是使用 EnumTypeHandler
枚举类型处理器将枚举的名称保存到数据库。若希望将枚举类型的索引值保存到数据库,则只需要在 MyBatis 的全局配置文件中指定使用 EnumOrdinalTypeHandler
枚举类型处理器即可,配置示例如下:
1 | <configuration> |
自定义枚举类型处理器的步骤
自定义枚举类型处理器的步骤如下:
- 1、实现
TypeHandler
接口或者继承BaseTypeHandler
类 - 2、使用
@MappedType
注解定义要处理的javaType
类型,使用@MappedJdbcTypes
注解定义要处理的jdbcType
类型,这一步骤可选 - 3、通过以下三种方式的任意一种来使用自定义的类型处理器
- 在参数处理的时候,声明要使用自定义的
TypeHandler
进行处理,例如:#{status, typeHandler=xxxx}
- 在 MyBatis 的全局配置文件中,指定
TypeHandler
要处理的javaType
类型,例如:<typeHandlers> <typeHandler handler="xxxx" javaType="xxxx"/> </typeHandlers>
- 在定义结果集标签的时候,声明要使用自定义的
TypeHandler
处理参数的封装,例如:<resultMap> <result column="status" property="status" typeHandler="xxxx"> </result> </resultMap>
- 在参数处理的时候,声明要使用自定义的
自定义枚举类型处理器的案例
本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-24
。
- 创建数据库表
1 | CREATE TABLE `t_admin` ( |
- JavaBean 类
1 | public class Admin { |
- 枚举类
1 | /** |
1 | /** |
- 枚举类型处理器
1 | import org.apache.ibatis.type.BaseTypeHandler; |
- Mapper 接口
1 | public interface AdminMapper { |
- SQL 映射文件
1 | <mapper namespace="com.clay.mybatis.dao.AdminMapper"> |
- MyBatis 的全局配置文件
1 | <configuration> |
- 业务代码
1 | public class MyBatisApplication { |
- 执行结果
1 | 21:36:08,853 DEBUG addAdmin:137 - ==> Preparing: insert into t_admin (name, email, status) values(?, ?, ?) |
观察上面的执行结果,可以发现 MyBatis 会自动将枚举类型的 value
属性保存到数据库中,而不是保存枚举类型的名称或者索引值,同时查询数据后也会自动封装枚举类型的数据。