1. MyBatis 框架分层架构

2. MyBatis 工作流程

  1. 获取 SqlSessionFactory 对象:

    • 解析配置文件(全局映射,Sql映射文件)的每一个信息,并保存在Configuration中,返回包含Configuration

      的DefaultSqlSession;
    • MappedStatement: 代表一个增删改查标签的详细信息;
  2. 获取 SqlSession 对象:
    • 返回一个DefaultSqlSession对象,包含Executor和Configuration;
  3. 获取接口的代理对象(MapperProxy)
    • getMapper()使用MapperProxyFactory创建一个MapperProxy的代理对象;
    • 代理对象中包含了DefaultSqlSession(Executor);
  4. 执行增删改查方法
    • 代理对象查询依赖DefaultSqlSession对象中的Executor,Executor创建StatementHandler对象,

      同时,创建ParameterHandler和ResultSetHandler对象,而ParameterHandler和ResultSetHandler都依赖TypeHandler;
    • StatementHandler: 设置sql语句,预编译,设置参数等相关工作,以及执行增删改查方法;
    • ParameterHandler: 设置预编译参数;
    • ResultHandler: 处理查询结果集;
    • TypeHandler: 在设置参数和处理查询结果时,都是依赖TypeHandler,进行数据库类型和javaBean类型的映射;

3. MyBatis 插件开发

  1. MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用.MyBatis支持对以下方法(四大对象)的拦截:

    • Executor
    • StatementHandler
    • ParameterHandler
    • ResultSetHandler
  2. 在创建四大对象的时候,并不是直接返回的,而是 interceptorChain.pluginAll(xxxHandler);
  3. pluginAll 方法就是获取到所有的Interceptor(插件需要实现的接口),调用interceptor.plugin(target),

    最终,返回包装后的target对象;
// pluginAll() 源码
public Object pluginAll(Object target){
for(Interceptor interceptor : interceptors){
target = interceptor.plugin(target);
}
return target;
}

3.1 插件的编写

  1. 步骤:

    • 编写Interceptor的实现类;
    • 使用@Intercepts注解完成插件签名;
    • 将写好的插件注册到全局配置文件中;
  2. 多个插件的执行顺序
    • 创建动态代理的时候,是按照插件的配置顺序,创建层层代理对象;
    • 执行目标方法的时候,按照逆序执行;

// 编写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)处理枚举

  1. 我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候,自定义参数封装策略;
  2. 步骤:
    • 实现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 工作流程及插件开发的更多相关文章

  1. springmvc 运行原理 Spring ioc的实现原理 Mybatis工作流程 spring AOP实现原理

    SpringMVC的工作原理图: SpringMVC流程 . 用户发送请求至前端控制器DispatcherServlet. . DispatcherServlet收到请求调用HandlerMappin ...

  2. Mybatis工作流程及其原理与解析

    Mybatis简介:    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBat ...

  3. mybatis(五)mybatis工作流程

    转载:https://www.cnblogs.com/wuzhenzhao/p/11103017.html 先来看一下MyBatis 的编程式使用的方法: public void testMapper ...

  4. mybatis工作流程&源码详解

    该篇主要讲解的是mybatis从seesion创建到执行sql语句的流程 流程主线: 1.创建SqlSessionFactoryBuilder 2.创建会话工厂SqlSessionFactory 3. ...

  5. mybatis工作流程

    1)通过Reader对象读取src目录下的mybatis.xml配置文件(该文本的位置和名字可任意) 2)通过SqlSessionFactoryBuilder对象创建SqlSessionFactory ...

  6. Mybatis第一篇【介绍、快速入门、工作流程】

    什么是MyBatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为 ...

  7. Mybatis的工作流程

    MyBatis工作流程 1:加载配置文件(mybatis-config.xml . *...Mapper.xml)并初始化, 将SQL的配置信息加载成为一个个MappedStatement对象(包括了 ...

  8. Mybatis工作原理(九)

    mybatis工作流程: (1) SqlSessionFactoryBuilder 从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例. (2) SqlSess ...

  9. MyBatis的几个重要概念和工作流程

    MyBatis 几个重要的概念 Mapper 配置: Mapper 配置可以使用基于 XML 的 Mapper 配置文件来实现,也可以使用基于 Java 注解的 MyBatis 注解来实现,甚至可以直 ...

随机推荐

  1. SQL Server 2012附加数据库报错

    操作系统: win8 数据库:SQL 2012 遇到问题: 以管理员身份登录SQL 2012,附件数据库提示如下错误: 解决办法: 以windows账号登录,附加,成功!

  2. EJB类库存在于Java的哪个版本中?(选择1项)。

    A. J2SE B. J2EE C. J2ME D. J2NE 解答:B

  3. $ -----JavaScript 中美元符号 $ 的作用

    JavaScript 中美元符号 $ 是什么 1.首先可以用来表示变量,比如变量 var s='asdsd'或var $s='asdasd'; 2.在正则表达式中,它可以匹配结尾:/sa$/.test ...

  4. backbone.js初探(转)

    BackBone是JavaScript frameworks for creating MVC-like web applications,最近流行的用来建立单页面web application的工具 ...

  5. php -- 魔术方法 之 对象输出 : __toString()

    对象输出:__toString() 当一个对象被当做字符串进行输出时(echo,print),会调用__toString()方法 <?php //输出对象 class Person{ //属性 ...

  6. linux改动登陆主机提示信息

    寻常管理着130多台Linux物理主机.真正搞清楚每一台主机的IP信息.应用部署比較麻烦! 所以在部署之初,必须规划好: 写一个脚本.把主机IP.管理员联系方法,应用部署等主机信息放在.sh里面 sh ...

  7. media wiki run on nginx

    1. 环境安装: nginx安装 nginx-1.5.7 php安装 PHP 5.4.10 (cli) (built: Jul 30 2014 16:45:08) mysql安装 Ver 14.14 ...

  8. IPL和SPL的区别

    IPL是英文Initial Program Loader的简称,意为初始程序的装入程序,其主要功能为负责主板.电源.硬件初始化程序.并把SPL装入RAM空间中,当IPL损坏则只能更换字库解决否则只能换 ...

  9. 将数据写入TXT文件中,file_put_contents与fwrite

    <?php header("content-type:text/html;charset=utf-8"); $file = './aa.txt'; ###判断是不是文件 if ...

  10. 运动目标检测ViBe算法

    一.运动目标检测简介   视频中的运动目标检测这一块现在的方法实在是太多了.运动目标检测的算法依照目标与摄像机之间的关系可以分为静态背景下运动检测和动态背景下运动检测.先简单从视频中的背景类型来讨论. ...