Mybatis允许在己映射语句执行过程中的某一点进行拦截调用。MyBatis 允许使用插件来拦截的接口和方法包括以下几个:

接口或方法 描述
Executor ( update、 query、 flushStatements、 commit、 rollback、getTransaction、 close、 isClosed)
ParameterHandler ( getParameterObject、 setParameters)
ResultSetHandler ( handleResultSets、 handleCursorResultSets、handleOutputParameters)
StatementHandler (prepare、 parameterize、 batch、 update、 query)

一、拦截器接口
自定义一个下画线键值转小写驼峰形式插件:
如:

package cn.linst.plugin;import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;import java.sql.Statement;
import java.util.*;/*** MyBatis Map 类型下画线 key 转小写驼峰形式*/
@Intercepts(@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})
)
@SuppressWarnings({"unchecked", "rawtypes"})
public class CameHumpInterceptor  implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 先执行得到结果,再对结果进行处理// invocation proceed()执行的结果被强制转换为了 List 类型// 拦截器接口 ResultSetHandler的 handleResultSets 方法的返回值为 List 类型,// 所以才能在这里直接强制转换。 许多接口方法的返回值类型都是 List ,但是还有很多其他的类型,// 所以在写拦截器时,要根据被拦截的方法来确定返回值的类型。List<Object> list = (List<Object>) invocation.proceed();for (Object object: list ) {if (object instanceof Map) {processMap((Map)object);} else {break;}}return list;}/*** 处理Map* @param map*/private void processMap(Map<String, Object> map) {Set<String> keySet = new HashSet<String>(map.keySet());for (String key : keySet) {//将以大写开头的字符串转换为小写,如采包含下画线也会处理为驼峰//此处只通过这两个简单的标识来判断是否进行转换if ((key.charAt(0)) >= 'A' && key.charAt(0) <= 'z' || key.indexOf("_") >= 0) {Object value = map.get(key);map.remove(key);map.put(underlineToCamelhump(key), value);}}}/*** 将下画线风格替换为驼峰风格* @param inputString* @return*/public static String underlineToCamelhump(String inputString) {StringBuilder sb = new StringBuilder() ;boolean nextUpperCase =false;for (int i=0; i < inputString.length(); i++) {char c = inputString.charAt(i);if (c == '_') {if (sb.length() > 0) {nextUpperCase = true;}} else {if (nextUpperCase) {sb.append(Character.toUpperCase(c));nextUpperCase = false;} else {sb.append(Character.toLowerCase(c));}}}return sb.toString();}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 其他配置--><plugins><plugin interceptor="cn.linst.plugin.CameHumpInterceptor"></plugin></plugins>
</configuration>

二、分页插件
PageHelper 分页插件。
表结构参考mybatis学习2

1、PageInterceptor 拦截器类
PageInterceptor:

package cn.linst.plugin;import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;@SuppressWarnings ({"rawtypes","unchecked"})
@Intercepts(@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class})
)
/*** Mybatis- 通用分页拦截器*/
public class PageInterceptor implements Interceptor {private static final List<ResultMapping> EMPTY_RESULTMAPPING = new ArrayList<ResultMapping>(0);private Dialect dialect;private Field additionalParametersField;@Overridepublic Object intercept(Invocation invocation) throws Throwable {//获取拦截方法的参数Object[] args = invocation . getArgs();MappedStatement ms = (MappedStatement) args[0];Object parameterObject = args[1] ;RowBounds rowBounds = (RowBounds) args[2] ;//调用方法判断是否需要进行分页,如果不需要,直接返回结果if (!dialect.skip(ms.getId() , parameterObject, rowBounds) ) {ResultHandler resultHandler = (ResultHandler) args[3];//当前的目标对象Executor executor = (Executor) invocation.getTarget();BoundSql boundSql = ms.getBoundSql(parameterObject);//反射获取动态参数Map<String, Object> additionalParameters = (Map<String, Object>)additionalParametersField.get(boundSql);//判断是否需要进行 count 查询if (dialect.beforeCount(ms.getId(), parameterObject, rowBounds)) {//根据当前的 ms 创建一个返回值为 Long 类型的 msMappedStatement countMs = newMappedStatement(ms, Long.class);//创建 count 查询的缓存 keyCacheKey countKey = executor.createCacheKey(countMs,parameterObject,RowBounds.DEFAULT,boundSql);//调用方言获取 count sqlString countSql = dialect.getCountSql(boundSql,parameterObject,rowBounds,countKey);BoundSql countBoundSql = new BoundSql(ms.getConfiguration(),countSql,boundSql.getParameterMappings(),parameterObject);//当使用动态 SQL 时,可能会产生临时的参数//这些参数需要手动设直到新的 BoundSqlfor (String key : additionalParameters.keySet()) {countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));}//执行 count 查询Object countResultList = executor.query(countMs,parameterObject,RowBounds.DEFAULT,resultHandler,countKey,countBoundSql);Long count = (Long) ((List) countResultList).get(0);//处理查询总数dialect.afterCount(count, parameterObject, rowBounds);if (count == 0L) {//当查询总数为0时,直接返回空的结果return dialect.afterPage(new ArrayList(),parameterObject,rowBounds);}}//判断是否需要进行分页查询if (dialect.beforePage(ms.getId(), parameterObject, rowBounds)) {//生成分页的缓存 keyCacheKey pageKey = executor.createCacheKey(ms,parameterObject,rowBounds,boundSql);//调用方言获取分页 sqlString pageSql = dialect.getPageSql(boundSql,parameterObject,rowBounds,pageKey);BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(),pageSql,boundSql.getParameterMappings(),parameterObject);//设直动态参数for (String key : additionalParameters.keySet()) {pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));}//执行分页查询List resultList = executor.query(ms,parameterObject,RowBounds.DEFAULT,resultHandler,pageKey,pageBoundSql);return dialect.afterPage(resultList, parameterObject, rowBounds);}}//返回默认查询return invocation.proceed() ;}/**** 根据现有的 ms 创建一个新的返回值类型,使用新的返回值类型* @param ms* @return resultType*/public MappedStatement newMappedStatement(MappedStatement ms , Class<?> resultType) {MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration() ,ms. getId() + "_Count",ms.getSqlSource() ,ms.getSqlCommandType()) ;builder.resource(ms.getResource());builder.fetchSize(ms.getFetchSize());builder.statementType(ms.getStatementType());builder.keyGenerator(ms.getKeyGenerator( ));if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {StringBuilder keyProperties = new StringBuilder();for (String keyProperty : ms.getKeyProperties()) {keyProperties.append(keyProperty).append(",");}keyProperties.delete(keyProperties.length() - 1 , keyProperties.length()) ;builder . keyProperty(keyProperties.toString());}builder.timeout(ms . getTimeout()) ;builder.parameterMap(ms.getParameterMap() );//count 查询返回值 intList<ResultMap> resultMaps =new ArrayList<ResultMap>() ;ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration() ,ms.getId() ,resultType ,EMPTY_RESULTMAPPING).build() ;resultMaps . add(resultMap);builder.resultMaps(resultMaps) ;builder.resultSetType(ms . getResultSetType()) ;builder.cache(ms . getCache()) ;builder.flushCacheRequired(ms . isFlushCacheRequired());builder.useCache(ms . isUseCache()) ;return builder.build() ;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {String dialectClass = properties.getProperty("dialect");try {dialect= (Dialect) Class.forName(dialectClass).newInstance();} catch (Exception e) {throw new  RuntimeException("使用 PageInterceptor 分页插件时,必须设直 dialect 属性");}dialect.setProperties(properties);try{//反射获取 boundSql 中的 additionalParameters 属性additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");additionalParametersField.setAccessible(true);} catch (NoSuchFieldException e) {throw new RuntimeException(e);}}
}

2、Dialect:数据库方言,针对不同数据库分别实现。

package cn.linst.plugin;import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.session.RowBounds;import java.util.List;
import java.util.Properties;/*** 数据库方言,针对不同数据库分别实现*/
@SuppressWarnings("rawtypes")
public interface Dialect {/***跳过 count 和分页查询*/boolean skip (String msId, Object parameterObject , RowBounds rowBounds);/*** 执行分页前,返回 true 会进行 count 查询,返回 false 会继续下面的 beforePage 判断* @param msId 执行的 MyBatis 方法会名* @param parameterObject 方法参数* @param rowBounds 分页参数* @return*/boolean beforeCount(String msId , Object parameterObject , RowBounds rowBounds);/*** 生成 unt 查询 sql* @param boundSql 绑定 SQL 对象* @param parameterObject 方法参数* @param rowBounds 分页参数* @param countKey count 缓存 key* @return*/String getCountSql(BoundSql boundSql , Object parameterObject , RowBounds rowBounds , CacheKey countKey);/*** 执行完 count 查询后* @param count 查询结采总数* @param parameterObject 接口参数* @param rowBounds 分页参数*/void afterCount(long count , Object parameterObject, RowBounds rowBounds);/*** 执行分页前,返回 true 会进行分页查询,返回 false 会返回默认查询结果* @param msId* @param parameterObject* @param rowBounds* @return*/boolean beforePage (String msId, Object parameterObject, RowBounds rowBounds);/*** 生成分页查询 sql* @param boundSql* @param parameterObject* @param rowBounds* @param pageKey* @return*/String getPageSql (BoundSql boundSql , Object parameterObject , RowBounds rowBounds , CacheKey pageKey) ;/*** 分页查询后,处理分页结果,拦截器 中直接 return 该方法的返回值* @param pageList 分页查询结果* @param parameterObject* @param rowBounds 分页参数* @return*/Object afterPage(List pageList, Object parameterObject , RowBounds rowBounds);/*** 设置参数* @param properties*/void setProperties(Properties properties) ;
}

3、MySqlDialect:MySQL 实现

package cn.linst.plugin;import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.session.RowBounds;import java.util.List;
import java.util.Properties;/***  MySQL 实现*/
@SuppressWarnings("rawtypes")
public class MySqlDialect implements Dialect{@Overridepublic boolean skip(String msId, Object parameterObject, RowBounds rowBounds) {//这里使用 RowBounds 分页// 没有 RowBounds 参数时,会使用 RowBounds DEFAULT 为默认值if(rowBounds != RowBounds.DEFAULT) {return false;}return true;}@Overridepublic boolean beforeCount(String msId, Object parameterObject, RowBounds rowBounds) {//只有使用 PageRowBounds 才能记录总数,否则查询了总数也没用if(rowBounds instanceof PageRowBounds) {return true;}return false ;}@Overridepublic String getCountSql(BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey countKey) {//简单欲套实现 MySQL count 查询return "select count(*) from ("+ boundSql . getSql() +") temp" ;}@Overridepublic void afterCount(long count, Object parameterObject, RowBounds rowBounds) {//记录总数,按照 beforeCount 逻辑,只有 PageRowBounds 才会查询 count,// 所以这里直接强制转换((PageRowBounds)rowBounds).setTotal(count);}@Overridepublic boolean beforePage(String msId, Object parameterObject, RowBounds rowBounds) {if(rowBounds != RowBounds . DEFAULT) {return true;}return false;}@Overridepublic String getPageSql(BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {// pageKey 会影响缓存,通过固定的 RowBounds 可以保证二级缓存有效pageKey.update ("RowBounds") ;return boundSql.getSql() +" limit "+ rowBounds.getOffset() + "," + rowBounds . getLimit();}@Overridepublic Object afterPage(List pageList, Object parameterObject, RowBounds rowBounds) {// 直接返回结果return pageList;}@Overridepublic void setProperties(Properties properties) {//没有其他参数}
}

4、PageRowBounds:可以记录 total 的分页参数

package cn.linst.plugin;import org.apache.ibatis.session.RowBounds;/*** 可以记录 total 的分页参数*/
public class PageRowBounds extends RowBounds {private long total;public PageRowBounds () {super();}public PageRowBounds(int offset , int limit) {super(offset, limit);}public long getTotal() {return total;}public void setTotal(long total){this.total = total;}
}

5、RoleMapper:接口

public interface RoleMapper {@Results(id="roleResultMap", value = {@Result(property = "id", column = "id", id = true),@Result(property = "roleName", column = "role_name"),@Result(property = "enabled", column = "enabled"),@Result(property = "createBy", column = "create_by"),@Result(property = "createTime", column = "create_time")})@ResultMap("roleResultMap")@Select("select * from sys_role")List<SysRole> selectAll(RowBounds rowBounds);}

6、mybatis-config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><plugins><!--<plugin interceptor="cn.linst.plugin.CameHumpInterceptor"></plugin>--><plugin interceptor="cn.linst.plugin.PageInterceptor"><property name="dialect" value="cn.linst.plugin.MySqlDialect"/></plugin></plugins><!-- 其他配置 -->
</configuration>

7、RoleMapperTest:

package cn.linst;import cn.linst.mapper.RoleMapper;
import cn.linst.model.SysPrivilege;
import cn.linst.model.SysRole;
import cn.linst.plugin.PageRowBounds;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.junit.Assert;
import org.junit.Test;import java.util.List;public class RoleMapperTest extends BaseMapperTest{@Testpublic void testSelectAllByRowBounds () {SqlSession sqlSession = getSqlSession();try {RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);//查询第 个,使用 RowBounds 类型时不会查询总数RowBounds rowBounds = new RowBounds(0, 1);List<SysRole> list = roleMapper.selectAll(rowBounds);for (SysRole role : list) {System.out.println("角色名:" + role.getRoleName());}//使用 PageRowBounds 时会查询总数PageRowBounds pageRowBounds = new PageRowBounds(0, 1);list = roleMapper.selectAll(pageRowBounds);//获取总数System.out.println("查询总数:" + pageRowBounds.getTotal());for (SysRole role : list) {System.out.println("角色名:" + role.getRoleName());}//再次查询获取第2个角pageRowBounds = new PageRowBounds(1, 1);list = roleMapper.selectAll(pageRowBounds);//获取总数System.out.println("查询总数:" + pageRowBounds.getTotal());for (SysRole role : list) {System.out.println("角色名:" + role.getRoleName());}} finally {sqlSession.close();}}//...
}

8、BaseMapperTest:

package cn.linst;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;import java.io.IOException;
import java.io.Reader;public class BaseMapperTest {private static SqlSessionFactory sqlSessionFactory;@BeforeClasspublic static void init () {try {Reader reader = Resources.getResourceAsReader("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);reader.close();} catch (IOException ex) {ex.printStackTrace();}}public SqlSession getSqlSession() {return sqlSessionFactory.openSession();}
}

运行结果:

DEBUG [main] - ==>  Preparing: select * from sys_role limit 0,1
DEBUG [main] - ==> Parameters:
TRACE [main] - <==    Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <==        Row: 1, 管理员, 1, 1, 2020-01-01 17:02:14.0
DEBUG [main] - <==      Total: 1
角色名:管理员
DEBUG [main] - ==>  Preparing: select count(*) from (select * from sys_role) temp
DEBUG [main] - ==> Parameters:
TRACE [main] - <==    Columns: count(*)
TRACE [main] - <==        Row: 2
DEBUG [main] - <==      Total: 1
查询总数:2
角色名:管理员
DEBUG [main] - ==>  Preparing: select * from sys_role limit 1,1
DEBUG [main] - ==> Parameters:
TRACE [main] - <==    Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <==        Row: 2, 普通用户, 1, 1, 2020-02-01 17:02:34.0
DEBUG [main] - <==      Total: 1
查询总数:2
角色名:普通用户

mybatis学习10相关推荐

  1. mybatis学习(10): sql server身份验证和windows身份验证

    SQL Server 支持两种身份验证模式,即 Windows 身份验证模式和混合模式. Windows 身份验证是默认模式(通常称为集成安全),因为此 SQL Server 安全模型与 Window ...

  2. 【转】MyBatis学习总结(四)——解决字段名与实体类属性名不相同的冲突

    [转]MyBatis学习总结(四)--解决字段名与实体类属性名不相同的冲突 在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定都是完全相同的,下面来演示一下这种情况下的如何解决字段名与实体 ...

  3. mybatis学习笔记(13)-延迟加载

    2019独角兽企业重金招聘Python工程师标准>>> mybatis学习笔记(13)-延迟加载 标签: mybatis [TOC] resultMap可以实现高级映射(使用asso ...

  4. MyBatis学习总结(二)——使用MyBatis对表执行CRUD操作

    上一篇博文MyBatis学习总结(一)--MyBatis快速入门中我们讲了如何使用Mybatis查询users表中的数据,算是对MyBatis有一个初步的入门了,今天讲解一下如何使用MyBatis对u ...

  5. MyBatis学习--简单的增删改查

    jdbc程序 在学习MyBatis的时候先简单了解下JDBC编程的方式,我们以一个简单的查询为例,使用JDBC编程,如下: 1 Public static void main(String[] arg ...

  6. Mybatis学习总结二

    Mybatis学习总结二 0.不需要修改的文件(在src下面) jdbc.properties文件 log4j.properties文件 SqlMapConfig.xml文件 1.高级结果映射 1.1 ...

  7. MyBatis学习总结一

    Mybatis学习总结一 1.Mybatis介绍 2.Mybatis架构图 2.1.架构图的意义 2.1.1.JDBC编写 2.1.2.反思问题 2.2.Mybatis架构图 3.入门案例 3.1.下 ...

  8. MyBatis学习随记

    1       Mybatis入门 1.1     单独使用jdbc编程问题总结 1.1.1  jdbc程序 Public static void main(String[] args) { Conn ...

  9. Java框架学习笔记--Mybatis学习

    一.Mybatis学习 1.Mybatis简介 简介:Mybatis开源免费框架.原名叫iBatis,2010在googlecode,2013年迁移到github2 作用:数据访问层框架.底层是对JD ...

最新文章

  1. 什么是图神经网络 (GNN)?
  2. MySQL中MyISAM引擎与InnoDB引擎性能简单测试
  3. date oracle 显示毫秒_Oracle date timestamp 毫秒 - 时间函数总结
  4. html页面js遍历listview,javascript实现的listview效果
  5. C语言课后习题(19)
  6. java编写正则表达式引擎_从0到1打造正则表达式执行引擎(一)
  7. 浙大PAT甲级1006
  8. [转载] Discrete Mathematics——04 一阶谓词逻辑基本概念
  9. 2020华为软件精英挑战赛初赛-Java版本
  10. 酷派android4.4.4到5.0,酷派大神F1电信版升级安卓5.0刷机教程[多图]
  11. SSD测试专题(二)
  12. 小码王python_小码王分享给Python初学者的几个技巧
  13. 中国版的日和-十万个冷笑话
  14. unreal编译源码搭建dedicated server的流水账——但是细
  15. 项目开发过程中遇到的问题和解决方法
  16. 一种锂电池充放电及外部供电自动切换的电路
  17. 【机器学习】逻辑回归(LogisticRegression)原理与实战
  18. Java安装WindowBuilder
  19. wp10手机不能连接微软服务器,Lumia920手机更新10166版WP10后,无法用Microsoft账户登录,也无法添加Microsoft账 - Microsoft Community...
  20. html5新特性(一)

热门文章

  1. JavaScript 介绍
  2. 2022 Pwnhub冬季赛 WP
  3. android更换开机动画,修改安卓开机动画(除了部分系统 如MIUI等)
  4. hualinux 1.25:Web开发技术发展史
  5. ChinaSoft 论坛巡礼 | 群智化软件测试技术和方法
  6. 重生后发现高冷女同桌暗恋我!(二)
  7. linux指法教程,linux系统指法练习与打字游戏软件
  8. SSH (安全shell 协议)
  9. python作排产计划表_生产排程计划表
  10. elementui表格宽度适应内容_解决elementui表格操作列自适应列宽