若有這樣的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) { } }