若有这样的dao方法:java
List<User> getUsersByIds(List<Long> idList) ; void deleteUsersByIds(List<Long> idList) ; void batchAddUsers(List<User> userList);
若参数为空的话,执行相关sql时,会报错,因是不完整的sql,以下所示:spring
select * from user where id in delete from user where id in insert into user(name,idcard,...) values
若想避免此一状况,能够在调用dao方法前进行判断。sql
if(idList!=null && !idList.isEmpty()){ //call dao here }
可否省去这种判断呢?若参数为空,不去执行sql不就好了吗。apache
刚开始想到用spring aop, 可是发觉不易实现,想拦截dao方法,但报错:数组
Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy27]: Common causes of this problem include using a final class or a non-visible class;
因dao均是接口,而其实现类自己是一个由mybatis实现的动态代理类,如今spring又要对该代理类进行代理,但mybatis生成的代理类又不能再被代理(理由见上面的错误提示)。session
而对servcie层进行代理处理起来也不方便,因参数不如dao方法直观,可能List参数是封装在一个业务对象中,如BussinessDTO.而且使用Spring Aop 还不通用(即每一个项目都要有本身的一套)。mybatis
最后想到了用Mybatis的拦截器,拦截其最后的执行sql类(org.apache.ibatis.executor.Executor的某个具体实现子类),判断输入参数是否为空,若为空,直接返回,不往下执行了。app
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }), @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) public class MyBatisCheckEmptyBeforeExecuteInterceptor implements Interceptor { //... }
注解定义须要拦截的方法。mybatis最终的执行方法只有两个,查询调用query方法,增、删、改均调用update方法。ide
具体拦截方法:this
Object parameter = invocation.getArgs()[1]; if (parameter == null) { // 参数值为null MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; Class parameterType = ms.getParameterMap().getType(); if (parameterType != null)// 实际存在输入参数 即并非无参方法 如getAll() return getDefaultReturnValue(invocation); //直接返回一个默认值(根据调用方法的返回类型) 如new ArrayList() } //若dao 方法的输入参数为List的话,mybatis会自动将其封转到一个map中 key为list(数组参数同样 但key为array) if (parameter instanceof Map) { Map map = (Map) parameter; //list参数是否为空或数组是否为空 if ((map.containsKey("list") && CollectionUtils.isEmpty((List) map.get("list"))) || (map.containsKey("array") && ArrayUtils.isEmpty((Object[]) map.get("array")))) return getDefaultReturnValue(invocation); } //参数非空 继续往下执行 return invocation.proceed();
完整代码:
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; /** * 执行sql前统一判空 如getByIds(List<Long> idList) 若输入参数为空 直接返回 再也不执行sql * * @author zhuguowei * */ @Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }), @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) public class MyBatisCheckEmptyBeforeExecuteInterceptor implements Interceptor { @SuppressWarnings("rawtypes") @Override public Object intercept(Invocation invocation) throws Throwable { Object parameter = invocation.getArgs()[1]; if (parameter == null) { // 参数值为空 MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; Class parameterType = ms.getParameterMap().getType(); if (parameterType != null)// 存在输入参数 return getDefaultReturnValue(invocation); } if (parameter instanceof Map) { Map map = (Map) parameter; if ((map.containsKey("list") && CollectionUtils.isEmpty((List) map .get("list"))) || (map.containsKey("array") && ArrayUtils .isEmpty((Object[]) map.get("array")))) return getDefaultReturnValue(invocation); } return invocation.proceed(); } /** * 获得默认返回值 * * @param invocation * @return */ @SuppressWarnings("rawtypes") private Object getDefaultReturnValue(Invocation invocation) { Class returnType = invocation.getMethod().getReturnType(); if (returnType.equals(List.class)) return new ArrayList(); else if (returnType.equals(Integer.TYPE)) return 0; return null; } /** * 只拦截Executor */ @Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { } }