引子
先看個技術(shù)題吧。
下面兩段代碼,執(zhí)行testFoo,結(jié)果分別是什么?
@Test public void testFoo() { foo(Arrays.asList(1, 2, 3)); } void foo(Object... args) { System.out.println(args); } |
@Test public void testFoo() { foo(Arrays.asList(1, 2, 3)); } void foo(Object... args) { System.out.println(args); } void foo(List list){ System.out.println(list); } ?文章來源地址http://www.zghlxwxcb.cn/news/detail-621643.html |
一眼看出來結(jié)果的同學(xué),恭喜你,本文內(nèi)容可以略過。
?
下面是正文。
?
springboot項目通常配合mybatisplus來做數(shù)據(jù)CRUD。
我們在查詢或更新數(shù)據(jù)的時候,有時要用到in來過濾數(shù)據(jù)。比如
SELECT * FROM emax_scbg_order WHERE order_no IN (1305679009380433922,1305405259472830465)
mybatisplus中關(guān)于in方法的使用,在傳多個字段值的時候,我們經(jīng)常搞不清是傳Array呢還是ArrayList呢?
其實,細(xì)心的同學(xué),看一下in方法的簽名定義,就明白了。
mybatisplus中有4個in方法的重載。
所有Wrapper的超類是AbstractWrapper,AbstractWrapper實現(xiàn)了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> 如果動態(tài)數(shù)組為 empty 則不會進(jìn)行 sql 拼接 </li> * * @param condition 執(zhí)行條件 * @param column 字段 * @param values 數(shù)據(jù)數(shù)組 * @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 則不會進(jìn)行 sql 拼接 </li> * * @param condition 執(zhí)行條件 * @param column 字段 * @param coll 數(shù)據(jù)集合 * @return children */ Children in(boolean condition, R column, Collection<?> coll); }
單從方法簽名以及清晰的javadoc注釋,我們可以看到,in方法接收字段值的方式有兩種,一種是Object...,一種是Collection<?>。
■ Collection<?>不用說了,是集合,比如List<E>、Set<E>、Queuet<E>等。
■ Object...是可變長參數(shù)(可變參數(shù)),可變長參數(shù)本質(zhì)上就是一個數(shù)組,既可以接收一個或多個離散的值,也可以接收數(shù)組對象。
也就是說,in方法同時支持傳入數(shù)組和集合。當(dāng)我們?nèi)雲(yún)⑹荓ist時,調(diào)用的是重載的in(Collection<?>),其他入?yún)⒎绞絼t是調(diào)用重載的in(Object...)。由此看來,調(diào)用mybatis-plus的in時,是傳Array還是傳List?就見分曉了。
現(xiàn)在,我們來做一個假設(shè):假設(shè)這些in重載方法里沒有in(Collection<?>),只有in(Object...),那么,我們應(yīng)用程序在調(diào)用的時候,當(dāng)需要in的參數(shù)值是一個集合時,如果我們把集合直接傳給in(Object...),那就是bug。因為在可變長參數(shù)里,集合是作為一個參數(shù)值的(不是多個)。也就是說,在這種假設(shè)下,程序生成的SQL會是?SELECT * FROM table1?WHERE state?IN ('[SUCCESS,FAIL]')?,我們的程序為規(guī)避這個bug,就要先把集合轉(zhuǎn)換為數(shù)組再調(diào)用in(Object...)。顯然,這樣會給我們的開發(fā)帶來額外的工作,更糟糕的是,這樣的bug很難徹底規(guī)避。
mybatis-plus框架的研發(fā)團隊顯然意識到了這個“假設(shè)”,故而增加了重載的in(Collection<?>),毋庸置疑是非常優(yōu)秀的設(shè)計。
下面詳細(xì)列舉使用in的姿勢。
?
使用in的姿勢
??正確姿勢一(List集合):
List<Long> ids = Arrays.asList(122L,23L);; new QueryWrapper<Driver>().lambda().in(Driver::getServiceId,ids);
?
??正確姿勢二(數(shù)組對象):
Long[] ids={1305679009380433922,1305679009380433922}; LambdaQueryWrapper<Driver> queryWrapper = new QueryWrapper<Driver>().lambda().in(Driver::getServiceId,ids);
?
??正確姿勢三(離散值):
new QueryWrapper<Driver>().lambda() .in(Driver::getServiceId,1305679009380433922,1305679009380433922);
?
正確結(jié)果:
==> Preparing: SELECT * FROM emax_scbg_order WHERE order_no IN (?,?) ==> Parameters: 1305679009380433922(String), 1305405259472830465(String) <== Total: 2
?
?
千萬別傳模棱兩可的參數(shù),這樣jvm會給你意想不到的結(jié)果。
??錯誤姿勢一:
.in( StringUtils.isNotBlank(vo.getOrderNumList()), ScbgOrder::getOrderNo, StringUtils.isNotBlank(vo.getOrderNumList()) ? vo.getOrderNumList().split(",") : "");
錯誤結(jié)果一:
==> Preparing: SELECT * FROM emax_scbg_order WHERE order_no IN (?)
==> Parameters: [Ljava.lang.String;@3eb6d7a9(String[])
<== Total: 0
調(diào)試程序可以看到values里的參數(shù)值:

?
?
??錯誤姿勢二:
.in(StringUtils.isNotBlank(vo.getOrderNumList()),ScbgOrder::getOrderNo,"123,4566");
?錯誤結(jié)果二:
==> Preparing: SELECT * FROM emax_scbg_order WHERE order_no IN (?)
==> Parameters: 123,4566(String)
<== Total: 0
OK,那么not in怎么用呢?
在mybatisplus中,not in的用法與in是相同的。如下notIn方法簽名的截圖一看便知:
話外:調(diào)用in出現(xiàn)NullPointerException,why?
下面代碼執(zhí)行到第9行時,拋出空指針異常??梢钥闯鰜?,這個in重載是public Children in(boolean condition, R column, Object... values)。開發(fā)同學(xué)疑惑:明明這個in的第一個參數(shù)判斷vo.getOprationType()是否為空了呀,為空就不執(zhí)行后面的第三個參數(shù)了,不為空才會執(zhí)行后面的邏輯呀。那么,即使vo.getOprationType()為null,也不應(yīng)該會拋空指針呀!
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時才使用第三個參數(shù)執(zhí)行sql處理”是in方法內(nèi)部的邏輯,而不是調(diào)用方的邏輯。調(diào)用方所做的事情是把參數(shù)值傳給in方法。所以, 當(dāng)vo.getOprationType()#split時,由于vo.getOprationType()是null,所以導(dǎo)致了空指針。
話外:對比spring-data-redis,再次表揚一下mybatisplus這些優(yōu)秀的in重載
spring-data-redis中的RedisTemplate類,提供了兩個execute方法,用來允許我們執(zhí)行LUA腳本。注意到了吧?這2個execute最后那個參數(shù)的類型是Object...。所以,在使用時,小心掉坑里,千萬別直接傳List哦。
文章來源:http://www.zghlxwxcb.cn/news/detail-621643.html
?
EOF,感謝閱讀!
到了這里,關(guān)于業(yè)務(wù)中臺如何實現(xiàn)業(yè)務(wù)的結(jié)果通知的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!