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. 在编写JSP的时候出现XXX cannot be resolved to a type

    今天遇到这个情况,却发现是eclipse抽风,说javax.servlet.http.Cookie找不到定义,但是经过浏览器测试,可以运行,而JSP源文件中eclipse死活要报错.表示无语. 关于e ...

  2. Integer类型的数据比较大小

    因为实体类用的是Integer包装类,所以是对象,不能直接比较大小, 一.一个Integer一个Int可以直接比较大小 二.两个Integer需要用.intValue()方法比较大小: 例如:cw.g ...

  3. 一些lua代码

    1.把k--v表转化为数组表,只支持2级 2.取中值 3.字符串按每行最多n像素分割,并返回每行最大宽度,可以用"\n"手动换行

  4. Linux下,PHP扩展安装(使用yum安装)

    直接操作linux,在命令模式下用yum 来安装PHP的扩展: 扩展:mysqli 命令: yum install php-mysqli 扩展:pdo 命令: yum install php-pdo

  5. java访问属性

  6. Unity的 Stats 窗体, Batched、SetPass、Draw Call 等

    孙广东  2015.8.12 在Game View 中的右上角有一个统计数据 Stats button.当按下button时.覆盖窗体显示,可用于优化性能的实时渲染统计信息. 确切的统计数据显示生成目 ...

  7. Python学习笔记6-字典Dict

    Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. >>> person ...

  8. axios如何进行跨域以及对返回格式为回调函数字符串的处理

    自从vue2.0开始不对vue-resouce进行维护了,转而用axios进行代替,axios的官方文档写的很详细,附上链接一枚:http://www.jianshu.com/p/df464b26ae ...

  9. 在scrollview中双击定点放大的代码

    双击放大是 iPhone 的一个基本操作,第三方程序里引入这一功能的话,主要是在 scrollview 呈现一张图片或者 PDF 页面时,双击可以放大,主要代码如下 - (void)scrollVie ...

  10. phpcms替换类列表页,内容页,主页

    phpcms替换类列表页,内容页,主页   利用phpcms制作企业站,首先要将静态的企业主页替换成后台可编辑的动态主页. 在phpcms/install_package/phpcms/templat ...