MyBatis @Param注解的實(shí)現(xiàn)
先說(shuō)結(jié)論:
當(dāng)輸入?yún)?shù)只有一個(gè)且沒(méi)有使用@Param注解時(shí),MyBatis會(huì)直接傳遞這個(gè)參數(shù);當(dāng)輸入?yún)?shù)多于一個(gè),或者使用了@Param注解時(shí),MyBatis會(huì)將參數(shù)封裝在Map中傳遞,這時(shí)的Map的key分為以下幾種可能:
- Map中會(huì)有param1, param2這樣的key,其順序?qū)?yīng)輸入?yún)?shù)的順序。無(wú)論是否有@Param注解。
- 對(duì)于@Param注解的參數(shù),Map中會(huì)保存注解中給定的名字作為key
- 對(duì)于沒(méi)有用@Param注解的參數(shù),Map中會(huì)用1、2、3 ..這樣的數(shù)字作為key,按順序保存輸入?yún)?shù)。
下面來(lái)看一下源碼。
首先,判斷一個(gè)方法中是否有用@Param注解的參數(shù):
private boolean hasNamedParams(Method method) { final Object[][] paramAnnos = method.getParameterAnnotations(); for (Object[] paramAnno : paramAnnos) { for (Object aParamAnno : paramAnno) { if (aParamAnno instanceof Param) { return true; } } } return false; }
如果有用@Param注解的參數(shù),取出注解中給出的參數(shù)名:
private String getParamNameFromAnnotation(Method method, int i, String paramName) { final Object[] paramAnnos = method.getParameterAnnotations()[i]; // 獲取第i個(gè)參數(shù)的注解 for (Object paramAnno : paramAnnos) { if (paramAnno instanceof Param) { paramName = ((Param) paramAnno).value(); break; } } return paramName; }
注意方法的輸入?yún)?shù),method表示是哪個(gè)方法上,i 表示第幾個(gè)參數(shù), paramName是傳進(jìn)來(lái)的參數(shù)名,如果該參數(shù)沒(méi)有用@Param注解,則返回傳進(jìn)來(lái)的paramName。
下面這個(gè)方法返回一個(gè)TreeMap(有序),其key表示參數(shù)的順序,比如key=0代表第0個(gè)參數(shù);value表示參數(shù)的名字,如果有用@Param注解標(biāo)注,則為標(biāo)注的參數(shù)名,否則和key相等,即用參數(shù)的序號(hào)作為參數(shù)的名字。
private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) { final SortedMap<Integer, String> params = new TreeMap<Integer, String>(); final Class<?>[] argTypes = method.getParameterTypes(); for (int i = 0; i < argTypes.length; i++) { if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) { String paramName = String.valueOf(params.size()); // 參數(shù)名,默認(rèn)為參數(shù)的序號(hào) if (hasNamedParameters) { //如果有使用@Param注解,則獲取注解標(biāo)注的參數(shù)名 paramName = getParamNameFromAnnotation(method, i, paramName); // 這里paramName作為參數(shù)傳進(jìn)來(lái),表示默認(rèn)值 } params.put(i, paramName); } } return params; }
其中hasNamedParameters只是從整個(gè)方法的維度,給出該方法是否有使用@Param注解的參數(shù);即使其值為true,具體到某一個(gè)參數(shù)上面,可能沒(méi)有使用@Param注解,因此調(diào)用getParamNameFromAnnotation傳入的paramName就作為默認(rèn)值返回,即參數(shù)的序號(hào)。
最后將調(diào)用方法的參數(shù)轉(zhuǎn)換為MyBatis內(nèi)部使用的參數(shù):
public Object convertArgsToSqlCommandParam(Object[] args) { final int paramCount = params.size(); if (args == null || paramCount == 0) { return null; } else if (!hasNamedParameters && paramCount == 1) { return args[params.keySet().iterator().next().intValue()]; } else { final Map<String, Object> param = new ParamMap<Object>(); int i = 0; for (Map.Entry<Integer, String> entry : params.entrySet()) { param.put(entry.getValue(), args[entry.getKey().intValue()]); // issue #71, add param names as param1, param2...but ensure backward compatibility final String genericParamName = "param" + String.valueOf(i + 1); if (!param.containsKey(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } }
其中args為Dao方法的輸入?yún)?shù),這里已經(jīng)轉(zhuǎn)換成了數(shù)組,其實(shí)就是動(dòng)態(tài)代理的invoke方法傳入的參數(shù)。
該方法首先對(duì)輸入?yún)?shù)進(jìn)行計(jì)數(shù),使用的params就是前面介紹的getParams方法的返回值。
if (!hasNamedParameters && paramCount == 1)
上面的條件判斷,即方法沒(méi)有使用@Param注解,且只有一個(gè)參數(shù),這時(shí)返回
args[params.keySet().iterator().next().intValue()]
即直接將其作為Object返回。
如果上面的條件不滿足的話,首先新建一個(gè)Map作為返回值:
final Map<String, Object> param = new ParamMap<Object>();
然后,設(shè)置map的key和value:
param.put(entry.getValue(), args[entry.getKey().intValue()]);
然后為了兼容性,做了如下操作
final String genericParamName = "param" + String.valueOf(i + 1); if (!param.containsKey(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); }
即設(shè)置param1、param2這樣的key。
到此為止,需要的參數(shù)對(duì)象Object就構(gòu)建完成,其中封裝了Dao傳入的多個(gè)參數(shù),并根據(jù)參數(shù)是否有@Param注解,影響了參數(shù)對(duì)象的類型(是否是map)。
參數(shù)封裝完成之后,下一步將其傳遞給SqlSession。
到此這篇關(guān)于MyBatis @Param注解的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)MyBatis @Param注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)Redis延時(shí)消息隊(duì)列
本文主要介紹了Java實(shí)現(xiàn)Redis延時(shí)消息隊(duì)列,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08在deepin上如何使用Fleet開(kāi)發(fā)SpringBoot?3.0.0項(xiàng)目
這篇文章主要介紹了在deepin上使用Fleet開(kāi)發(fā)SpringBoot?3.0.0項(xiàng)目的過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09Jmeter多用戶并發(fā)壓力測(cè)試過(guò)程圖解
這篇文章主要介紹了Jmeter多用戶并發(fā)壓力測(cè)試過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07ShardingSphere jdbc集成多數(shù)據(jù)源的實(shí)現(xiàn)步驟
本文主要介紹了ShardingSphere jdbc集成多數(shù)據(jù)源的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10ElasticSearch學(xué)習(xí)之Es集群Api操作示例
這篇文章主要為大家介紹了ElasticSearch學(xué)習(xí)之Es集群Api操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01spring事務(wù)之事務(wù)掛起和事務(wù)恢復(fù)源碼解讀
這篇文章主要介紹了spring事務(wù)之事務(wù)掛起和事務(wù)恢復(fù)源碼解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11基于Java的打包jar、war、ear包的作用與區(qū)別詳解
本篇文章,小編為大家介紹,基于Java的打包jar、war、ear包的作用與區(qū)別詳解。需要的朋友參考下2013-04-04