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 注解来实现,甚至可以直 ...
随机推荐
- 在编写JSP的时候出现XXX cannot be resolved to a type
今天遇到这个情况,却发现是eclipse抽风,说javax.servlet.http.Cookie找不到定义,但是经过浏览器测试,可以运行,而JSP源文件中eclipse死活要报错.表示无语. 关于e ...
- Integer类型的数据比较大小
因为实体类用的是Integer包装类,所以是对象,不能直接比较大小, 一.一个Integer一个Int可以直接比较大小 二.两个Integer需要用.intValue()方法比较大小: 例如:cw.g ...
- 一些lua代码
1.把k--v表转化为数组表,只支持2级 2.取中值 3.字符串按每行最多n像素分割,并返回每行最大宽度,可以用"\n"手动换行
- Linux下,PHP扩展安装(使用yum安装)
直接操作linux,在命令模式下用yum 来安装PHP的扩展: 扩展:mysqli 命令: yum install php-mysqli 扩展:pdo 命令: yum install php-pdo
- java访问属性
- Unity的 Stats 窗体, Batched、SetPass、Draw Call 等
孙广东 2015.8.12 在Game View 中的右上角有一个统计数据 Stats button.当按下button时.覆盖窗体显示,可用于优化性能的实时渲染统计信息. 确切的统计数据显示生成目 ...
- Python学习笔记6-字典Dict
Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. >>> person ...
- axios如何进行跨域以及对返回格式为回调函数字符串的处理
自从vue2.0开始不对vue-resouce进行维护了,转而用axios进行代替,axios的官方文档写的很详细,附上链接一枚:http://www.jianshu.com/p/df464b26ae ...
- 在scrollview中双击定点放大的代码
双击放大是 iPhone 的一个基本操作,第三方程序里引入这一功能的话,主要是在 scrollview 呈现一张图片或者 PDF 页面时,双击可以放大,主要代码如下 - (void)scrollVie ...
- phpcms替换类列表页,内容页,主页
phpcms替换类列表页,内容页,主页 利用phpcms制作企业站,首先要将静态的企业主页替换成后台可编辑的动态主页. 在phpcms/install_package/phpcms/templat ...