MyBatis 工作流程及插件开发
1. MyBatis 框架分层架构

2. MyBatis 工作流程
- 获取 SqlSessionFactory 对象:
- 解析配置文件(全局映射,Sql映射文件)的每一个信息,并保存在Configuration中,返回包含Configuration
的DefaultSqlSession; MappedStatement: 代表一个增删改查标签的详细信息;
- 解析配置文件(全局映射,Sql映射文件)的每一个信息,并保存在Configuration中,返回包含Configuration
- 获取 SqlSession 对象:
- 返回一个DefaultSqlSession对象,包含Executor和Configuration;
- 获取接口的代理对象(MapperProxy)
getMapper()使用MapperProxyFactory创建一个MapperProxy的代理对象;- 代理对象中包含了DefaultSqlSession(Executor);
- 执行增删改查方法
- 代理对象查询依赖DefaultSqlSession对象中的Executor,Executor创建StatementHandler对象,
同时,创建ParameterHandler和ResultSetHandler对象,而ParameterHandler和ResultSetHandler都依赖TypeHandler; - StatementHandler: 设置sql语句,预编译,设置参数等相关工作,以及执行增删改查方法;
- ParameterHandler: 设置预编译参数;
- ResultHandler: 处理查询结果集;
- TypeHandler: 在设置参数和处理查询结果时,都是依赖TypeHandler,进行数据库类型和javaBean类型的映射;
- 代理对象查询依赖DefaultSqlSession对象中的Executor,Executor创建StatementHandler对象,

3. MyBatis 插件开发
- MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用.MyBatis支持对以下方法(四大对象)的拦截:
- Executor
- StatementHandler
- ParameterHandler
- ResultSetHandler
- 在创建四大对象的时候,并不是直接返回的,而是
interceptorChain.pluginAll(xxxHandler); - pluginAll 方法就是获取到所有的Interceptor(插件需要实现的接口),调用
interceptor.plugin(target),
最终,返回包装后的target对象;
// pluginAll() 源码
public Object pluginAll(Object target){
for(Interceptor interceptor : interceptors){
target = interceptor.plugin(target);
}
return target;
}
3.1 插件的编写
- 步骤:
- 编写Interceptor的实现类;
- 使用
@Intercepts注解完成插件签名; - 将写好的插件注册到全局配置文件中;
- 多个插件的执行顺序
- 创建动态代理的时候,是按照插件的配置顺序,创建层层代理对象;
- 执行目标方法的时候,按照逆序执行;

// 编写Interceptor实现类: MyFirstPlugin.java
// @Intercepts 注解: 为当前插件指定要拦截哪个对象的哪个方法,以及方法中的参数
@Intercepts(
{
@Signature(type=StatementHandler.class,method="parameterize",
args=java.sql.Statement.class)
}
)
public class MyFirstPlugin implements Interceptor{
// 拦截目标对象中目标方法的执行
public Object intercept(Invocation invocation) throws Throwable{
// 执行目标方法
Object proceed = invocation.proceed();
// 返回拦截之后的目标方法
return proceed;
}
// 包装目标对象,即为目标对象创建一个代理对象
public Object plugin(Object target){
// 借助 Plugin 的 wrap(Object target,Interceptor interceptor); 包装我们的目标对象
// target: 目标对象, interceptor: 拦截器, this 表示使用当前拦截器
Object proxy = Plugin.wrap(target,this);
return proxy;
}
// 可以获取插件注册时,传入的property属性
public void setProperties(Propreties properties){
System.out.println("插件的配置信息:"+properties);
}
}
// 在全局配置文件: mybatis-config.xml 中注册插件
<plugins>
<!-- interceptor: 拦截器的类路径 -->
<plugin interceptor="cn.itcast.mybatis.dao.MyFirstPlugin">
<property name="username" value="zhangsan"/>
</plugin>
</plugins>
4. 使用 PageHelper 插件进行分页
// 测试类: 查询所有员工
public class MyBatisTest{
@Test
public void test() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
// 参数说明: 1 表示第一页数据, 3 表示每页3条数据
Page<Object> page = PageHelper.startPage(1,3);
List<Employee> emps = mapper.getEmps();
for(Employee emp : emps){
System.out.println(emp);
}
System.out.println("当前页码:"+page.getPageNum());
System.out.println("总记录数:"+page.getTotal());
System.out.println("每页记录数:"+page.getPageSize());
System.out.println("总页码:"+page.getPages());
} finally{
openSession.close();
}
}
}
// 在mybatis-plugin中注册PageHelper插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
5. 批量操作
// 测试类: 批量保存员工
public class MyBatisTest{
@Test
public void test() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
// 向数据库中插入10000条数据
for(int i=0; i<10000; i++){
mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0,5),
"zhangsan@163.com","1");
}
openSession.commit();
}finally{
openSession.close();
}
}
6. 自定义类型处理器(TypeHandler)处理枚举
- 我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候,自定义参数封装策略;
- 步骤:
- 实现TypeHandler接口或者继承BaseTypeHandler;
- 使用
@MappedTypes定义处理的java类型;
使用@MappedJdbcTypes定义jdbcType类型; - 在自定义结果集标签或者参数处理的时候,声明使用自定义 TypeHandler 进行处理
或者在全局配置自定义TypeHandler;
// Employee.java
public class Employee{
private Integer id;
private String lastName;
private String email;
private String gender;
// 枚举类型: 用户状态包括登录,登出,不存在
// 用户默认状态:用户登出
private EmpStatus empStatus=EmpStatus.LOGOUT;
get 和 set 方法(略)
}
// EmpStatus.java
public enum EmpStatus{
LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在")
}
// EmployMapper.xml
<!-- 保存客户 -->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name,email,gender,empStatus)
values(#{lastName},#{email},#{gender},#{empStatus})
</insert>
// mybatis-config.xml
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="cn.itcast.mybatis.bean.EmpStatus"/>
</typeHandlers>
// 测试类
public class MyBatisTest{
@Test
public void test() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee("test_enum","zhangsan@163.com","1");
// MyBatis 在处理枚举对象时,默认保存的是枚举的名字: EnumTypeHandler
// 也可以使用枚举的索引来保存:EnumOrdinalTypeHandler
//
mapper.addEmp(employee);
System.out.println("保存成功"+employee.getId());
openSession.commit();
}finally{
openSession.close();
}
@Test
public void testEnumUse(){
EmpStatus login = EmpStatus.LOGIN;
System.out.println("枚举的索引:"+login.ordinal());
System.out.println("枚举的名字:"+login.name());
}
}
// 升级版: 数据库保存的是 100, 200等这些自定义的状态码,而不是枚举的索引或者枚举的名字
// EmpStatus.java
public enum EmpStatus{
LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在");
private Integer code; // 状态码
private String msg; // 枚举的提示信息
// 有参构造函数
private EmpStatus(Integer code, String msg){
this.code = code;
this.msg = msg;
}
get 和 set 方法(略)
// 按照状态码,返回枚举对象
public static EmpStatus getEmpStatusByCode(Integer code){
switch(code){
case 100:
return LOGIN;
case 200:
return LOGOUT;
case 300:
return REMOVE;
default:
return LOGOUT;
}
}
}
// 自定义枚举处理器
public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus>{
//定义当前数据如何保存到数据库中
public void setParameter(PreparedStatement ps, int i, EmpStatus parameter,
JdbcType jdbcType) throws SQLException{
ps.setString(i,parameter.getCode().toString());
}
//从数据库中获取到枚举状态码,返回一个枚举对象
public EmpStatus getResult(ResultSet rs, String columnName) throws SQLException{
int code = rs.getInt(columnName);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
public EmpStatus getResult(ResultSet rs, String columnIndex) throws SQLException{
int code = rs.getInt(columnIndex);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
public EmpStatus getResult(CallableStatement cs, String columnIndex) throws SQLException{
int code = cs.getInt(columnIndex);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
}
// mybatis-config.xml 中配置自定义枚举处理器
<typeHandlers>
<typeHandler handler="cn.itcast.mybatis.typehandler.MyEnumEmpStatusTypeHandler"
javaType="cn.itcast.mybatis.bean.EmpStatus"/>
</typeHandlers>
// 测试类
public class MyBatisTest{
@Test
public void testEnumUse(){
EmStatus login = EmpStatus.LOGIN;
System.out.println("枚举的索引:"+login.ordinal());
System.out.println("枚举的名字:"+login.name());
System.out.println("枚举的状态码:"+login.getCode());
System.out.println("枚举的提示信息:"+login.getMsg());
}
@Test
public void testEnum() throws IOException{
同上;
}
}
参考资料
MyBatis 工作流程及插件开发的更多相关文章
- springmvc 运行原理 Spring ioc的实现原理 Mybatis工作流程 spring AOP实现原理
SpringMVC的工作原理图: SpringMVC流程 . 用户发送请求至前端控制器DispatcherServlet. . DispatcherServlet收到请求调用HandlerMappin ...
- Mybatis工作流程及其原理与解析
Mybatis简介: MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBat ...
- mybatis(五)mybatis工作流程
转载:https://www.cnblogs.com/wuzhenzhao/p/11103017.html 先来看一下MyBatis 的编程式使用的方法: public void testMapper ...
- mybatis工作流程&源码详解
该篇主要讲解的是mybatis从seesion创建到执行sql语句的流程 流程主线: 1.创建SqlSessionFactoryBuilder 2.创建会话工厂SqlSessionFactory 3. ...
- mybatis工作流程
1)通过Reader对象读取src目录下的mybatis.xml配置文件(该文本的位置和名字可任意) 2)通过SqlSessionFactoryBuilder对象创建SqlSessionFactory ...
- Mybatis第一篇【介绍、快速入门、工作流程】
什么是MyBatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为 ...
- Mybatis的工作流程
MyBatis工作流程 1:加载配置文件(mybatis-config.xml . *...Mapper.xml)并初始化, 将SQL的配置信息加载成为一个个MappedStatement对象(包括了 ...
- Mybatis工作原理(九)
mybatis工作流程: (1) SqlSessionFactoryBuilder 从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例. (2) SqlSess ...
- MyBatis的几个重要概念和工作流程
MyBatis 几个重要的概念 Mapper 配置: Mapper 配置可以使用基于 XML 的 Mapper 配置文件来实现,也可以使用基于 Java 注解的 MyBatis 注解来实现,甚至可以直 ...
随机推荐
- js 静态方法 静态变量 实例方法 实例变量
1.静态方法的定义 Js代码 var BaseClass = function() {}; // var BaseClass=new Function(); BaseClass.f1 = func ...
- Android中<uses-sdk>属性和target属性分析
1. 概要 <uses-sdk> 用来描述该应用程序可以运行的最小和最大API级别,以及应用程序开发者设计期望运行的平台版本.通过在manifest清单文件中添加该属性,我们可以更好的控制 ...
- Unity3D学习(九):C#和C++的相互调用
前言 不知不觉已经一年了,这一年来一直忙于公司项目疯狂加班,很少有自己的时间写下东西.不过好在项目最近也步入正轨了,正好抽空写点东西记录下学到的一些东西. 公司项目是一个端游IP移植手游,端游是基于C ...
- msf提权命令/meterpreter下的几个命令
废话: 今天本来日学校内网.以为是台08.结果稀里糊涂居然日了宿舍哥们儿的PC机.按道理都该装杀毒的才对,我舍友都不装的.裸装上阵说的就是我舍友了.劝各位大佬.把杀毒装好.补丁打好. 通过这次我也学到 ...
- 深入分析 iBATIS 框架之系统架构与映射原理
iBATIS 框架主要的类层次结构 总体来说 iBATIS 的系统结构还是比较简单的,它主要完成两件事情: 根据 JDBC 规范建立与数据库的连接: 通过反射打通 Java 对象与数据库参数交互之间相 ...
- javascript父、子页面交互小结
帧用来存放子页面,既可以是iframe,又可以是frameset.window对象是全局对象,页面上的一切函数和对象都在它的作用域里. 1.parent代表父窗口.如果父窗口又存在若干层嵌套, ...
- Hadoop中HDFS工作原理
转自:http://blog.csdn.net/sdlyjzh/article/details/28876385 Hadoop其实并不是一个产品,而是一些独立模块的组合.主要有分布式文件系统HDFS和 ...
- 关于Unity的坐标系
1.坐标系分为左手坐标系和右手坐标系 2.用手从X轴旋到Y轴画一个弧,如果大拇指所指的方向是Z轴,那么这个坐标系就是这只手的坐标系 3.Unity是左手坐标系,OpenGl是右手坐标系 Unity坐标 ...
- 使用OpenFace进行人脸识别(2)
http://blog.csdn.net/u011531010/article/details/52270023 http://www.vccoo.com/v/2ed520 第一步 在 openfac ...
- MFC常见错误提示:opened in another editor
有时候在使用MFC的过程中常常会遇到这种提示.假设你在看想必你也遇到这种烦恼. 没办法-- 把打开的文件所有关闭.然后你就发现期待的RC文件出现了! .!!!.!!!!!!! ! !.! !! .