springboot項目通常配合mybatisplus來做數據CRUD。 我們在查詢或更新數據的時候,有時要用到in來過濾數據。比如SELECT * FROM emax_scbg_order WHERE order_no IN (1305679009380433922,130540525947283 ...
springboot項目通常配合mybatisplus來做數據CRUD。
我們在查詢或更新數據的時候,有時要用到in來過濾數據。比如
SELECT * FROM emax_scbg_order WHERE order_no IN (1305679009380433922,1305405259472830465)
mybatisplus中關於in方法的使用,在傳多個欄位值的時候,我們經常搞不清是傳Array呢還是ArrayList呢?
其實,細心的同學,看一下in方法的定義,就明白了。
mybatisplus中有4個in方法的重載。
所有Wrapper的超類是AbstractWrapper,AbstractWrapper實現了Func<Children, R>介面。in方法主要在Func<Children, R>介面中定義。
下麵是Func<Children, R>介面中in方法的4個重載:
//mybatis-plus-core-3.1.2.jar package com.baomidou.mybatisplus.core.conditions.interfaces; /** * 查詢條件封裝 * * @author hubin miemie HCL * @since 2017-05-26 */ @SuppressWarnings("unchecked") public interface Func<Children, R> extends Serializable { /** * ignore */ default Children in(R column, Collection<?> coll) { return in(true, column, coll); } /** * ignore */ default Children in(R column, Object... values) { return in(true, column, values); } /** * 欄位 IN (v0, v1, ...) * <p>例: in("id", 1, 2, 3, 4, 5)</p> * * <li> 如果動態數組為 empty 則不會進行 sql 拼接 </li> * * @param condition 執行條件 * @param column 欄位 * @param values 數據數組 * @return children */ default Children in(boolean condition, R column, Object... values) { return in(condition, column, Arrays.stream(Optional.ofNullable(values).orElseGet(() -> new Object[]{})) .collect(toList())); } /** * 欄位 IN (value.get(0), value.get(1), ...) * <p>例: in("id", Arrays.asList(1, 2, 3, 4, 5))</p> * * <li> 如果集合為 empty 則不會進行 sql 拼接 </li> * * @param condition 執行條件 * @param column 欄位 * @param coll 數據集合 * @return children */ Children in(boolean condition, R column, Collection<?> coll); }
我們可以看到,in方法接收欄位值的方式,一種是Object...,一種是Collection<?>。
■ Collection<?>不用說了,是集合,比如Listt<E>、Sett<E>、Queuet<E>等。
■ Object...是可變長參數(可變參數),可變長參數本質上就是一個數組,既可以接收一個或多個離散的值,也可以接收數組對象。
也就是說,in方法同時支持傳入數組和集合。取決於你調用哪個重載方法。
使用in的姿勢
正確姿勢一(List集合):
List<Long> ids = Arrays.asList(122L,23L);; new QueryWrapper<Driver>().lambda().in(Driver::getServiceId,ids);
正確姿勢二(數組對象):
Long[] ids={1305679009380433922,1305679009380433922}; LambdaQueryWrapper<Driver> queryWrapper = new QueryWrapper<Driver>().lambda().in(Driver::getServiceId,ids);
正確姿勢三(離散值):
new QueryWrapper<Driver>().lambda() .in(Driver::getServiceId,1305679009380433922,1305679009380433922);
正確結果:
==> Preparing: SELECT * FROM emax_scbg_order WHERE order_no IN (?,?) ==> Parameters: 1305679009380433922(String), 1305405259472830465(String) <== Total: 2
千萬別傳模棱兩可的參數,這樣jvm會給你意想不到的結果。
錯誤姿勢一:
.in(
StringUtils.isNotBlank(vo.getOrderNumList()),
ScbgOrder::getOrderNo,
StringUtils.isNotBlank(vo.getOrderNumList()) ? vo.getOrderNumList().split(",") : "");
錯誤結果一:
==> Preparing: SELECT * FROM emax_scbg_order WHERE order_no IN (?)
==> Parameters: [Ljava.lang.String;@3eb6d7a9(String[])
<== Total: 0
調試程式可以看到values里的參數值:

錯誤姿勢二:
.in(StringUtils.isNotBlank(vo.getOrderNumList()),ScbgOrder::getOrderNo,"123,4566");
錯誤結果二:
==> Preparing: SELECT * FROM emax_scbg_order WHERE order_no IN (?)
==> Parameters: 1,3(String)
<== Total: 0
OK,那麼not in怎麼用呢?
在mybatisplus中,not in的用法與in是相同的。如下notIn方法簽名的截圖一看便知:
話外:調用in出現NullPointerException,why?
下麵代碼執行到第9行時,拋出空指針異常。可以看出來,這個in重載是public Children in(boolean condition, R column, Object... values)。開發同學疑惑:明明這個in的第一個參數判斷vo.getOprationType()是否為空了呀,為空就不執行後面的第三個參數了,不為空才會執行後面的邏輯呀。那麼,即使vo.getOprationType()為null,也不應該會拋空指針呀!
1 private LambdaQueryWrapper<PayMerchantOpenFlow> getPayMerchantOpenFlowQueryWrapperByVO(PayMerchantOpenFlowDTO vo){ 2 LambdaQueryWrapper<PayMerchantOpenFlow> wrapper = new QueryWrapper<PayMerchantOpenFlow>().lambda() 3 .eq(StringUtils.isNotBlank(vo.getMerchantCode()), PayMerchantOpenFlow::getMerchantCode,vo.getMerchantCode()) 4 .eq(null != vo.getRelationId(), PayMerchantOpenFlow::getRelationId,vo.getRelationId()) 5 .eq(StringUtils.isNotBlank(vo.getStatus()), PayMerchantOpenFlow::getStatus,vo.getStatus()) 6 .eq(StringUtils.isNotBlank(vo.getMerchantName()), PayMerchantOpenFlow::getMerchantName,vo.getMerchantName()) 7 .eq(StringUtils.isNotBlank(vo.getPayChannelCode()), PayMerchantOpenFlow::getPayChannelCode,vo.getPayChannelCode()) 8 .eq(StringUtils.isNotBlank(vo.getPayChannelName()), PayMerchantOpenFlow::getPayChannelName,vo.getPayChannelName()) 9 .in(StringUtils.isNotBlank(vo.getOprationType()), PayMerchantOpenFlow::getOprationType,vo.getOprationType().split(",")) 10 .between(StringUtils.isNoneBlank(vo.getCreateTimeBegin(), vo.getCreateTimeEnd()), PayMerchantOpenFlow::getCreateTime, vo.getCreateTimeBegin() + " 00:00:01", vo.getCreateTimeEnd() + " 23:59:59") 11 .orderByDesc(PayMerchantOpenFlow::getCreateTime); 12 return wrapper; 13 }
答案是:上面的“第一個條件為true時才使用第三個參數執行sql處理”是in方法內部的邏輯,而不是調用方的邏輯。調用方所做的事情是把參數值傳給in方法。所以, 當vo.getOprationType()#split時,由於vo.getOprationType()是null,所以導致了空指針。
當看到一些不好的代碼時,會發現我還算優秀;當看到優秀的代碼時,也才意識到持續學習的重要!--buguge
本文來自博客園,轉載請註明原文鏈接:https://www.cnblogs.com/buguge/p/17595281.html