如有这样的dao方法:
List<User> getUsersByIds(List<Long> idList) ;
void deleteUsersByIds(List<Long> idList) ;
void batchAddUsers(List<User> userList);
若参数为空的话,执行相关sql时,会报错,因是不完整的sql,如下所示:
select * from user where id in
delete from user where id in
insert into user(name,idcard,...) values
若想避免此一情况,可以在调用dao方法前进行判断。
if(idList!=null && !idList.isEmpty()){
//call dao here
}
能否省去这种判断呢?若参数为空,不去执行sql不就行了吗。
刚开始想到用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生成的代理类又不能再被代理(理由见上面的错误提示)。
而对servcie层进行代理处理起来也不方便,因参数不如dao方法直观,可能List参数是封装在一个业务对象中,如BussinessDTO.并且使用Spring Aop 还不通用(即每个项目都要有自己的一套)。
最后想到了用Mybatis的拦截器,拦截其最后的执行sql类(org.apache.ibatis.executor.Executor的某个具体实现子类),判断输入参数是否为空,若为空,直接返回,不往下执行了。
@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方法。
具体拦截方法:
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) {
}
}