通过拦截器Interceptor优化Mybatis的in查询
来源:互联网 发布:apache poi word 编辑:程序博客网 时间:2024/06/16 07:39
本文首发于简书:http://www.jianshu.com/p/e20323d960f4
在工作中,我们经常会因为在mybatis中的不严谨写法,导致查询语句会产生in()的情况。这种情况不符合SQL的语法,导致程序报错。
网上有一些解决方案,大部分的解决方案都是对list判null和判空来处理。
<if test="list != null and list.size>0"> do something</if>
但是这种解决方法会产生一个逻辑问题,本来in一个空列表,查询结果应该是没有数据才对,现在却变成了这个in条件失效,这就导致了结果有可能并不是我们想要的。
还有一种解决方案是对list做双重判断。第一重判断和上面的解决方案一致,增加的第二重判断是为了保证如果list为空列表则只能查到空列表
<if test="list != null and list.size>0"> do something</if>
<if test="list!=null and list.size==0"> and 1=0</if>
这种方案能解决in()的问题报错的问题,也不会产生逻辑错误的情况。但是这个写法有点繁琐,每次遇到这种情况都需要特殊判断。
于是我就准备通过拦截器Interceptor来解决这个问题。
在理想状态下,我们在xml中应该只应该判断list非null就可以了。
以下是我写的EmptyCollectionIntercept
@Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class})})public class EmptyCollectionIntercept implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //通过invocation.getArgs()可以得到当前执行方法的参数 //第一个args[0]是MappedStatement对象,第二个args[1]是参数对象parameterObject。 final Object[] args = invocation.getArgs(); MappedStatement mappedStatement = (MappedStatement) args[0]; Object parameter = args[1]; if (parameter == null) { Class parameterType = mappedStatement.getParameterMap().getType(); // 实际执行时的参数值为空,但mapper语句上存在输入参数的异常状况,返回默认值 if (parameterType != null) { return getDefaultReturnValue(invocation); } return invocation.proceed(); } BoundSql boundSql = mappedStatement.getBoundSql(parameter); if (isHaveEmptyList(boundSql.getSql())) { return getDefaultReturnValue(invocation); } return invocation.proceed(); } @Override public Object plugin(Object target) { //只拦截Executor对象,减少目标被代理的次数 if (target instanceof Executor) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { } /** * 返回默认的值,list类型的返回空list,数值类型的返回0 * * @param invocation * @return */ private Object getDefaultReturnValue(Invocation invocation) { Class returnType = invocation.getMethod().getReturnType(); if (returnType.equals(List.class)) { return Lists.newArrayList(); } else if (returnType.equals(Integer.TYPE) || returnType.equals(Long.TYPE) || returnType.equals(Integer.class) || returnType.equals(Long.class)) { return 0; } return null; } /** * 去除字符中的干扰项,如果存在in(" in")这种情况会将" in"替换成"" * * @param sql * @return */ private static String removeInterference(String sql) { Pattern pattern = Pattern.compile("\".*?\""); Matcher matcher = pattern.matcher(sql); while (matcher.find()) { String replaceWorld = matcher.group(); if (StringUtils.containsIgnoreCase(replaceWorld, " in")) { sql = sql.replace(replaceWorld, "\"\""); } } return sql; } /** * 判断sql是否存在in空列表,找到in关键字,判断in后面是否没有括号或者括号中没有元素 * * @param sql * @return */ private static Boolean isHaveEmptyList(String sql) { sql = removeInterference(sql); Boolean isHaveEmptyList = Boolean.FALSE; List<Integer> indexList = Lists.newArrayList(); Pattern pattern = Pattern.compile("\\s(?i)In"); Matcher matcher = pattern.matcher(sql); while (matcher.find()) { indexList.add(matcher.start()); } if (CollectionUtils.isEmpty(indexList)) { return isHaveEmptyList; } Pattern p2 = Pattern.compile("(?<=\\()(.+?)(?=\\))"); for (Integer index : indexList) { if(StringUtils.isEmpty(subSql)){ isHaveEmptyList = Boolean.TRUE; break; } String subSql = sql.substring(index + 3); Boolean flag = subSql.startsWith("(") || subSql.startsWith(" ") || subSql.startsWith("\n") || subSql.startsWith("\r"); if (!flag) { continue; } subSql = subSql.trim(); if (!subSql.startsWith("(")) { isHaveEmptyList = Boolean.TRUE; break; } Matcher m2 = p2.matcher(subSql); if (m2.find()) { if (StringUtils.isEmpty(m2.group())) { isHaveEmptyList = Boolean.TRUE; break; } } } return isHaveEmptyList; }}
基本上看代码都能看懂,就不一一解释了。
如何配置mybatis的拦截器大家可以自行研究哦。
需要代码的同学可以在保留版权的情况下自行获取,欢迎大家给我提宝贵的建议哈。
阅读全文
0 0
- 通过拦截器Interceptor优化Mybatis的in查询
- mybatis 拦截器Interceptor 笔记
- Mybatis通过拦截器实现分页查询
- mybatis-interceptor:Spring动态拦截器
- Mybatis那些事-拦截器(Plugin+Interceptor)
- MyBatis之拦截器interceptor学习
- Mybatis Interceptor 拦截器原理 源码分析
- struts2的拦截器(3):通过实现Interceptor接口定义自己的拦截器
- struts2的拦截器(Interceptor)
- SpringMVC的interceptor拦截器
- 拦截器Interceptor的使用
- SpringMVC的拦截器Interceptor
- Struts2的拦截器interceptor
- SpringMVC的拦截器Interceptor
- struts2的拦截器Interceptor
- OkHttp的拦截器Interceptor
- MyBatis 插件之拦截器(Interceptor)实现原理
- struts2开发时通过interceptor拦截器实现输入数据过滤前后空格的功能
- linux下的多线程编程
- 自定义View滑动开关
- Windows下获得窗口实例句柄的3种方法
- 4.7 Java中的包 package
- 关于java.sql.SQLException: No suitable driver问题的解决
- 通过拦截器Interceptor优化Mybatis的in查询
- Android中串口通讯的问题
- Java线程池执行的任务抛出异常看不到日志
- Java开发新手上路三个月
- Ubuntu 16.04 apt-get更换为国内阿里云源
- 笔记大全
- 文章标题
- CSV文件介绍 及 文件读写工具类
- HTML标签学习5