通過攔截器Interceptor優化Mybatis的in查詢
在工作中,我們經常會因為在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("(?<=\\()[.\n]*?(?=\\))");
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.isBlank(m2.group())) {
isHaveEmptyList = Boolean.TRUE;
break;
}
}
}
return isHaveEmptyList;
}
}
基本上看程式碼都能看懂,就不一一解釋了。
如何配置mybatis的攔截器大家可以自行研究哦。
需要程式碼的同學可以在保留版權的情況下自行獲取,歡迎大家給我提寶貴的建議哈。
相關文章
- Mybatis Interceptor 攔截器MyBatis
- 攔截器(Interceptor)與過濾器(Filter)過濾器Filter
- Java interceptor 攔截器Java
- 過濾器 Filter 與 攔截器 Interceptor 的區別過濾器Filter
- MyBatis攔截器MyBatis
- Mybatis 攔截器MyBatis
- Solon 的過濾器 Filter 和兩種攔截器 Handler、 Interceptor過濾器Filter
- Okhttp的Interceptor攔截器原始碼解析HTTP原始碼
- SpringMVC中使用Interceptor攔截器SpringMVC
- Mybatis中的攔截器MyBatis
- SpringMVC中的攔截器Interceptor實現SpringMVC
- gRPC(六)進階:攔截器 interceptorRPC
- mybatis註冊攔截器MyBatis
- Mybatis-分頁攔截器MyBatis
- MyBatis攔截器優雅實現資料脫敏MyBatis
- 微信域名攔截查詢 如何批次查詢我們的域名在微信中是否被攔截
- OkHttp 3.x 原始碼解析之Interceptor 攔截器HTTP原始碼
- Springboot通過攔截器攔截請求資訊收集到日誌Spring Boot
- 攔截過濾器模式過濾器模式
- 解決在Interceptor攔截器中使用@DubboReference注入為nullNull
- Mybatis 分頁:Pagehelper + 攔截器實現MyBatis
- “犯罪心理”解讀 Mybatis 攔截器MyBatis
- "犯罪心理"解讀Mybatis攔截器MyBatis
- 查詢優化器優化
- struts struts攔截器(過濾器)過濾器
- 攔截器,攔截器棧總結
- 關於 SAP Spartacus Angular HTTP Interceptor 的攔截順序AngularHTTP
- 使用攔截器的方法過濾特性
- Spring 過濾器和攔截器Spring過濾器
- 微信域名攔截檢測 查詢微信中域名是否被攔截的最新方法
- spring中的過濾器與攔截器Spring過濾器
- 【轉】攔截器與過濾器的區別過濾器
- JavaInterceptingFilterPattern(攔截過濾器)JavaAIFilter過濾器
- Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高階配置”SpringMVC
- Mybatis Plus 通過QueryWrapper做查詢時in()方法的使用MyBatisAPP
- 聊一聊過濾器與攔截器過濾器
- 談談 Spring 的過濾器和攔截器Spring過濾器
- springBoot的過濾器,監聽器,攔截器Spring Boot過濾器