
Mybatis篇-(十)插件开发与扩展(MyBatis实用场景)
发布日期:2021-05-14 16:24:27
浏览次数:27
分类:精选文章
本文共 10287 字,大约阅读时间需要 34 分钟。
十、Mybatis-插件开发与扩展(MyBatis实用场景)
插件开发
- MyBatis在四大对象的创建过程中,都会有插件进行 介入。插件可以利用动态代理机制一层层的包装目标 对象,而实现在目标对象执行目标方法之前进行拦截 的效果
- MyBatis 允许在已映射语句执行过程中的某一点进行 拦截调用
- 默认情况下,MyBatis 允许使用插件来拦截的方法调 用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
插件原理
在四大对象创建的时候
- 每个创建出来的对象不是直接返回的,而是 要执行interceptorChain.pluginAll(parameterHandler);
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
- 获取到所有的Interceptor(拦截器)(插件需要实现的接口); 调用interceptor.plugin(target);返回target包装后的对象
- 插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
- 我们的插件可以为四大对象创建出代理对象
- 代理对象就可以拦截到四大对象的每一个执行
插件的使用
- 编写插件实现Interceptor接口,并使用 @Intercepts注解完成插件签名
/** * 完成插件签名: * 告诉MyBatis当前插件用来拦截哪个对象的哪个方法 */@Intercepts( { @Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class) })public class MyFirstPlugin implements Interceptor{ /** * intercept:拦截: * 拦截目标对象的目标方法的执行; */ @Override public Object intercept(Invocation invocation) throws Throwable { // TODO Auto-generated method stub System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod()); //执行目标方法 Object proceed = invocation.proceed(); //返回执行后的返回值 return proceed; } /** * plugin: * 包装目标对象的:包装:为目标对象创建一个代理对象 */ @Override public Object plugin(Object target) { // TODO Auto-generated method stub //我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象 System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target); Object wrap = Plugin.wrap(target, this); //返回为当前target创建的动态代理 return wrap; } /** * setProperties: * 将插件注册时 的property属性设置进来 */ @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub System.out.println("插件配置的信息:"+properties); }}
- 在全局配置文件中注册插件
- 运行结果
若配置了多个拦截器拦截同一个对象方法

小案例
动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询11号员工
只需要修改MyFirstPlugin中的intercept方法
/** * intercept:拦截: * 拦截目标对象的目标方法的执行; */ @Override public Object intercept(Invocation invocation) throws Throwable { // TODO Auto-generated method stub System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod()); //动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工 Object target = invocation.getTarget(); System.out.println("当前拦截到的对象:"+target); //拿到:StatementHandler==>ParameterHandler===>parameterObject //拿到target的元数据 MetaObject metaObject = SystemMetaObject.forObject(target); Object value = metaObject.getValue("parameterHandler.parameterObject"); System.out.println("sql语句用的参数是:"+value); //修改完sql语句要用的参数 metaObject.setValue("parameterHandler.parameterObject", 11); //执行目标方法 Object proceed = invocation.proceed(); //返回执行后的返回值 return proceed; }
运行结果

扩展( MyBatis实用场景 )
分页插件PageHelper
说明:
使用步骤
-
导入jar包
-
在MyBatis全局配置文件中配置分页插件
- 使用PageHelper提供的方法进行分页
@Test public void test01() throws IOException { // 1、获取sqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); // 2、获取sqlSession对象 SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); //第一个参数 pageNum(显示第几页) //第二个参数 pageSize(每页显示多少页) Page
运行效果

- 可以使用更强大的PageInfo封装返回结果
@Test public void test02() throws IOException { // 1、获取sqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); // 2、获取sqlSession对象 SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Page
运行效果

批量处理
概述
默认的 openSession() 方法没有参数,它会创建有如下特性的
- 会开启一个事务(也就是不自动提交)
- 连接对象会从由活动环境配置的数据源实例得到
- 事务隔离级别将会使用驱动或数据源的默认设置
- 预处理语句不会被复用,也不会批量处理更新
openSession 方法的 ExecutorType 类型的参数,枚举类型
- ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情(这是默认装配 的)
- 它为每个语句的执行创建一个新的预处理语句
- ExecutorType.REUSE: 这个执行器类型会复用预处理语句
- ExecutorType.BATCH: 这个执行器会批量执行所有更新语句
批量处理的使用
批量操作我们是使用MyBatis提供的BatchExecutor进行的, 他的底层就是通过jdbc攒sql的方式进行的。我们可以让他 攒够一定数量后发给数据库一次
测试
核心代码就是
SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
@Test public void testBatch() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); //可以执行批量操作的sqlSession SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); for (int i = 0; i < 10000; i++) { mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1")); } openSession.commit(); }finally{ openSession.close(); } }
总结:
批量处理:(预编译sql一次==>设置参数===>10000次===>执行(1次)) 耗时4598
非批量:(预编译sql=设置参数=执行)==》10000次 耗时10200
存储过程
以Oracle为例
使用案例
xml
JavaBean
public class PageEmp { private int start; private int end; private int count; private Listemps; }
创建存储过程

测试
/** * oracle分页: * 借助rownum:行号;子查询; * 存储过程包装分页逻辑 * @throws IOException */ @Test public void testProcedure() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); OraclePage page = new OraclePage(); page.setStart(1); page.setEnd(5); mapper.getPageByProcedure(page); System.out.println("总记录数:"+page.getCount()); System.out.println("查出的数据:"+page.getEmps().size()); System.out.println("查出的数据:"+page.getEmps()); }finally{ openSession.close(); } }
自定义类型处理器(枚举为例)
我们可以通过自定义TypeHandler的形式来在设置参数或 者取出结果集的时候自定义参数封装策略
测试
枚举类
/** * 希望数据库保存的是100,200这些状态码,而不是默认0,1或者枚举的名 * @author lfy * */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; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } //按照状态码返回枚举对象 public static EmpStatus getEmpStatusByCode(Integer code){ switch (code) { case 100: return LOGIN; case 200: return LOGOUT; case 300: return REMOVE; default: return LOGOUT; } }}
JavaBean
public class Employee { private Integer id; private String lastName; private String email; private String gender; //员工状态 private EmpStatus empStatus=EmpStatus.LOGOUT; ...}
自定义类型处理器
/** * 1、实现TypeHandler接口。或者继承BaseTypeHandler * @author lfy * */public class MyEnumEmpStatusTypeHandler implements TypeHandler{ /** * 定义当前数据如何保存到数据库中 */ @Override public void setParameter(PreparedStatement ps, int i, EmpStatus parameter, JdbcType jdbcType) throws SQLException { // TODO Auto-generated method stub System.out.println("要保存的状态码:"+parameter.getCode()); ps.setString(i, parameter.getCode().toString()); } @Override public EmpStatus getResult(ResultSet rs, String columnName) throws SQLException { // TODO Auto-generated method stub //需要根据从数据库中拿到的枚举的状态码返回一个枚举对象 int code = rs.getInt(columnName); System.out.println("从数据库中获取的状态码:"+code); EmpStatus status = EmpStatus.getEmpStatusByCode(code); return status; } @Override public EmpStatus getResult(ResultSet rs, int columnIndex) throws SQLException { // TODO Auto-generated method stub int code = rs.getInt(columnIndex); System.out.println("从数据库中获取的状态码:"+code); EmpStatus status = EmpStatus.getEmpStatusByCode(code); return status; } @Override public EmpStatus getResult(CallableStatement cs, int columnIndex) throws SQLException { // TODO Auto-generated method stub int code = cs.getInt(columnIndex); System.out.println("从数据库中获取的状态码:"+code); EmpStatus status = EmpStatus.getEmpStatusByCode(code); return status; }}
全局配置文件配置类型转换器
测试插入
@Test public void testEnum() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee employee = new Employee("test_enum", "enum@atguigu.com","1"); mapper.addEmp(employee); System.out.println("保存成功"+employee.getId()); openSession.commit(); }finally{ openSession.close(); } }
运行结果

测试查询
@Test public void testEnum() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee employee = new Employee("test_enum", "enum@atguigu.com","1"); Employee empById = mapper.getEmpById(10012); System.out.println(empById.getEmpStatus()); }finally{ openSession.close(); } }
运行结果

手动修改数据库中的状态码再查询

发表评论
最新留言
留言是一种美德,欢迎回访!
[***.207.175.100]2025年04月30日 17时42分54秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
LeetCode 45/55. 跳跃游戏I/II
2021-05-14
老鸟带你画tiled lines
2021-05-14
MybatisPlus自定义Sql实现多表查询
2021-05-15
Java位运算,负数的二进制表示形式,int类型最大值为什么是2的31次方-1
2021-05-15
WIFI模块开发教程之W600网络篇3:STA模式下TCP Client通信
2021-05-15
PyQt5快速上手基础篇10-QSettings用法
2021-05-15
JQuery--手风琴,留言板
2021-05-15
vue--搜索,添加,删除小案例
2021-05-15
VUE框架应用包---------微信二维码应用
2021-05-15
(KOA2 step1)利用koa-generator创建KOA2项目
2021-05-15
MFC 自定义消息发送字符串
2021-05-15
goahead 下goaction测试与搭建
2021-05-15
Adding Powers
2021-05-15
不能将 "const char *" 类型的值分配到 "char *" 类型的实体
2021-05-15
PyTorch学习笔记——(6)数据加载Dataset和DataLoader的使用
2021-05-15
ideal 下创建springboot项目
2021-05-15
Linux操作系统的安装与使用
2021-05-15
ajax请求出现/[object%20Object]错误的解决办法
2021-05-15
01背包(小偷的概率)
2021-05-15
流体运动估计光流算法研究
2021-05-15