日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MyBatis处理多参数及原理分析

發布時間:2024/9/30 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MyBatis处理多参数及原理分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、多參數處理方式

1.1使用@Param注解

MyBatis 允許在mapper 接口中使用@Param注解來處理多個參數。

mapper 接口:

/** @Param 注解中的值可以是任意的 */Employee getEmpByIdAndUsername(@Param("id") Integer id, @Param("username") String username);

SQL 映射文件:

<!-- 這里#{} 中的id 與username,對應上面@Param 注解中的值 --><select id="getEmpByIdAndUsername" resultType="employee">select * from t_employee where id = #{id} and username = #{username}</select>

1.2使用Map封裝多個參數

MyBatis 允許使用Map來封裝需要在SQL 中傳入的參數。

mapper 接口:

Employee getEmpByMap(Map<String, Object> map);

SQL 映射文件:

<!-- #{} 中的值要與在map 中定義的key 一致 --><select id="getEmpByMap" resultType="employee">select * from t_employee where id = #{id} and username = #{username}</select>

測試代碼,使用Map封裝傳遞的參數:

// 用來封裝在SQL 語句中需要傳遞的參數Map<String, Object> map = new HashMap<>();map.put("id", 1);map.put("username", "張三");Employee employee = employeeMapper.getEmpByMap(map);

1.3使用#{param1 ... n}參數

如果我們既不用@Param也不使用map,那么可以在編寫SQL 語句的時候,使用#{param1 ... n}來代表需要傳遞的參數。

mapper 接口:

Employee getEmpByIdAndGender(Integer id, String username);

SQL 映射文件:

<!-- 通過使用#{param1 ... n}的方式來傳遞參數 --><select id="getEmpByIdAndGender" resultType="employee">select * from t_employee where id = #{param1} and gender = #{param2}</select>

1.4發散思維

比如遇到下面的幾種情況時,我們要能夠隨機應變:

/** 第一個參數加@Param 注解,后面一個參數不加 */Employee getEmpByIdAndEmail(@Param("id") Integer id, String email);/** 編寫SQL 參數對應方式 */id ==> #{param1}或id, email ==> #{param2} /** 第二個參數是一個JavaBean 類型 */Employee getEmpByIdAndEmail(Integer id,@Param("employee") Employee email);/** 編寫SQL 參數對應方式 */id ==> #{param1}, email ==> #{param2.email} 或employee.email

1.5建議與小結

建議:在書寫多參數的SQL 語句時,如果需要傳遞的參數都是某個JavaBean 的屬性,那么建議傳入單個的JavaBean,通過JavaBean 來封裝需要傳遞的參數。

比如改寫Employee getEmpByIdAndGender(Integer id, String username);方法。

mapper 接口:

Employee getEmpByIdAndGender(Employee employee);

SQL 映射文件:

<!-- 在select 標簽中指定parameterType(傳進來的參數類型)是某個JavaBean 類型,也可以省略#{} 中的參數要與對應的JavaBean 的屬性一致--><select id="getEmpByIdAndGender" parameterType="employee" resultType="employee">select * from t_employee where id = #{id} and gender = #{gender}</select>

小結:在傳入單個參數中,在編寫SQL 語句時#{ }中的值可以是任意的,因為MyBatis 會自動對這個參數進行處理。在傳遞多個參數時,其實MyBatis 也會自動對多參數進行處理,會將這些參數在底層封裝成一個Map。下面就通過DEBUG 的方式來一探究竟。

二、多參數封裝成Map原理解析

這里我們以#{param1 ... n}傳遞參數為例,也就是上面1.3 所使用的方式,下面是其測試代碼(省略了getSqlSession()方法):

@Testpublic void parameterTest() throws IOException {SqlSession sqlSession = getSqlSession();EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee = employeeMapper.getEmpByIdAndGender(15, "1");System.out.println(employee);}

1.將斷點打在Employee employee = employeeMapper.getEmpByIdAndGender(15, "1");上,會跳入到MapperProxy<T>類中的invoke(Object proxy, Method method, Object[] args)方法。

/** MapperProxy<T>類中的方法 */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}

在執行到最后一行return mapperMethod.execute(sqlSession, args);時,下面是其對應的參數信息:

2.接著打斷點執行會跳到MapperMethod類中的execute(SqlSession sqlSession, Object[] args)方法,在這個方法中會對SQL 語句的類型進行判斷,因為是SELECT語句,為了防止多余的代碼太多,故刪除了一些用不到的信息。

/** MapperMethod 類中的方法 */ public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}/** 省略了UPDATE 與DELETE 的情況*/case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {/** * 因為只查詢一條信息,所以會進入到這里,method.convertArgsToSqlCommandParam(args)* 方法會對參數進行轉換,下面繼續跟蹤,看看是如何進行參數轉換的*/Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}

3.接著跟蹤,會跳到MapperMethod類中的convertArgsToSqlCommandParam(Object[] args) 方法,其中args中任然保存著傳入的參數信息。

/** MapperMethod 類中的方法 */public Object convertArgsToSqlCommandParam(Object[] args) {return paramNameResolver.getNamedParams(args);}

4.接著打斷點,會跳到ParamNameResolver類中的getNamedParams(Object[] args)方法,根據類名我們應該知道了,應該就是在這個類中對參數進行轉換的。

/** ParamNameResolver 類中的方法 */public Object getNamedParams(Object[] args) {final int paramCount = names.size();if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {return args[names.firstKey()];} else {final Map<String, Object> param = new ParamMap<Object>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...)final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);// ensure not to overwrite parameter named with @Paramif (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}}

在上面這個方法中有一個names屬性,這個names是什么呢?names 其實是一個全局變量,并且是一個Map,Map中的key用于保存索引,value就是要保存的參數名。names通過ParamNameResolver類的構造函數來實例化存儲信息。接著就進入到重點部分了。下面是ParamNameResolver類的構造函數:

/*** 下面是官方說明的names 存儲規則:* 如果使用注解,則names 的key 保存索引,value 保存注解中的值* 不使用下標,names 中key 保存索引,value 保存的也是參數的索引* 特殊的參數在這里我們就不考慮了* * <li>aMethod(@Param("M") int a, @Param("N") int b) -&gt; {{0, "M"}, {1, "N"}}</li>* <li>aMethod(int a, int b) -&gt; {{0, "0"}, {1, "1"}}</li>* <li>aMethod(int a, RowBounds rb, int b) -&gt; {{0, "0"}, {2, "1"}}</li>* </ul>*/private final SortedMap<Integer, String> names; /** ParamNameResolver 類的構造函數 */public ParamNameResolver(Configuration config, Method method) {final Class<?>[] paramTypes = method.getParameterTypes();final Annotation[][] paramAnnotations = method.getParameterAnnotations();final SortedMap<Integer, String> map = new TreeMap<Integer, String>();int paramCount = paramAnnotations.length;// get names from @Param annotationsfor (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {if (isSpecialParameter(paramTypes[paramIndex])) {// skip special parameterscontinue;}String name = null;for (Annotation annotation : paramAnnotations[paramIndex]) {/*** 對當前注解判斷,如果是Param 注解,就把Param 注解中的值賦給name,* 因為我們沒有使用注解,所以name 為null*/if (annotation instanceof Param) {hasParamAnnotation = true;name = ((Param) annotation).value();break;}}if (name == null) {// 如果沒有指定@Param 注解,在這里通過getActualParamName(method, paramIndex) 方法為name 賦值if (config.isUseActualParamName()) {// 這里就不細節講述是如何獲得name 的值的了,有興趣的可以自己查看源碼進行了解name = getActualParamName(method, paramIndex);}// 到這里name 已經有值了,會直接跳過if (name == null) {// use the parameter index as the name ("0", "1", ...)// gcode issue #71name = String.valueOf(map.size());}// 在map 中存儲信息,key 表示索引,value 為獲得的namemap.put(paramIndex, name);}// 為names 賦值names = Collections.unmodifiableSortedMap(map);}

下面是執行完構造函數后,names中保存的信息:

其實在給names的value賦值時,還加了arg前綴。PS:這里我們在進行測試的時候并沒有使用@Param注解,如果使用的是@Param注解,那么names 的value保存的應該就是在@Param中設置的值。

分析完names中保存的是什么信息后,我們就可以對上面的getNamedParams(Object[] args)方法進行解析了。

/** ParamNameResolver 類中的方法 */public Object getNamedParams(Object[] args) {// 此時paramCount 為2final int paramCount = names.size();// 如果為空,直接返回if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {// 如果沒有使用注解并且只有一個參數,直接返回return args[names.firstKey()];} else {final Map<String, Object> param = new ParamMap<Object>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {/*** 遍歷循環,把names 的value作為param 的key,names 的key 作為param 的value* 存儲為{"arg0",0},{"arg1",1} 的格式*/param.put(entry.getValue(), args[entry.getKey()]);// GENERIC_NAME_PREFIX 的值是"param",繼續進行處理,genericParamName 會是param1 ... n final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);/*** 把genericParamName 作為key,傳遞進來的SQL參數作為value* 存儲為{"param1", "傳遞進來的參數"} 的格式* 這樣做的好處是即使你使用了@Param注解,你也可以通過使用#{param1 ... n}的方式來進行參數傳遞*/if (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}// 最后返回paramreturn param;}}

最終param中保存的數據為:

5.接著一步步返回,返回的時候還進行了很多步驟,最終會執行到
result = sqlSession.selectOne(command.getName(), param);,
也就是返回到MapperedMethod中的(SqlSession sqlSession, Object[] args)方法,到這里就可以把信息查詢出來了。

三、總結

這篇博客主要介紹了MyBatis 處理多參數的方法,以及需要注意的地方。學習不僅要知其然還要知其所以然,所以在博文的后半部分通過DEBUG 的方式對MyBatis 處理多參數封裝成Map的原理進行了分析。建議大家自己動手去分析源碼的底層,去了解更多的有趣的知識,希望這篇博文能夠為讀者提供幫助。

總結

以上是生活随笔為你收集整理的MyBatis处理多参数及原理分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 天天操天天干天天插 | 中文字幕在线观看二区 | av在线天天 | 久久久久久免费精品 | 五月网站 | www.欧美在线观看 | 探花国产精品一区二区 | 午夜爽爽影院 | 最好看的中文字幕国语电影mv | 国产精品成av人在线视午夜片 | 精品一区二区三区四区五区六区 | 99久久国产热无码精品免费 | 亚洲精品~无码抽插 | 涩涩涩av| jizz视频在线观看 | 超91在线| 打白嫩屁屁网站视频短裙 | 美女被草视频在线观看 | 精品无码一区二区三区的天堂 | 天天综合色 | 日韩和欧美一区二区 | 成人国产在线观看 | 农村黄色片 | 久久午夜国产精品 | 亚洲一级二级片 | 男女aa视频| 中文字幕一区二区三区精华液 | 天天爽夜夜爽 | aaaa一级片 | 不卡久久 | 爽天天天天天天天 | 亚洲三级视频在线观看 | 色av中文字幕 | 性综合网 | 国产伦精品一区二区三区妓女下载 | 久久中文字幕av | 免费高清欧美大片在线观看 | 办公室荡乳欲伦交换bd电影 | 亚洲综合激情五月久久 | 国产一级黄 | 国产大片b站 | 免费黄色小网站 | 最近中文字幕在线中文高清版 | 精品福利三区3d卡通动漫 | 四虎永久网址 | 天天做天天爱天天做 | 久久成人国产精品 | 中文亚洲字幕 | 欧美三级一区二区三区 | 女人裸体又黄 | 久久丫丫 | 欧美黄色a级片 | 亚洲国产小视频 | 成人精品在线看 | 神马香蕉久久 | 国产在线一区二区 | 钰慧的mv视频在线观看 | 激情文学久久 | 成人精品亚洲 | 亚洲天堂一区二区在线观看 | 日本wwww色 | 包射屋 | 丰满人妻一区二区三区53号 | 亚洲精品 日韩无码 | 黑人干日本少妇 | 富婆如狼似虎找黑人老外 | 粉嫩aⅴ一区二区三区 | 黄色精品一区二区 | 日韩伦人妻无码 | 超碰超碰超碰超碰超碰 | 国产大片aaa | 国产精品九九 | 污污的视频在线免费观看 | 91伦理 | 黑人与日本少妇高潮 | 伊人精品在线视频 | 九九免费视频 | 亚洲精品美女久久久 | 国产福利精品在线观看 | 狠狠视频| 亚洲色图偷 | 亚洲成人黄色网址 | 波多野结衣视频免费在线观看 | 国产精选第一页 | 日日夜操| 在线观看9.1 | 波多野结衣视频网址 | 日韩中文在线观看 | 欧美亚洲自拍偷拍 | 学生调教贱奴丨vk | 高跟鞋丝袜猛烈xxxx | 中日韩在线播放 | 日韩av高清 | 无码人妻av免费一区二区三区 | 国产手机看片 | 欧美不卡视频 | 一区二区三区国 | 黄色裸体网站 | 香蕉网久久 |