MyBatis中動(dòng)態(tài)SQL語(yǔ)句@Provider的用法
前言
Mybatis里的動(dòng)態(tài)SQL,估計(jì)用到的同學(xué)不是很多,畢竟在xml文件中定義sql語(yǔ)句的方式,已經(jīng)可以滿足絕大部分的開發(fā)需求,方便又簡(jiǎn)單。沒(méi)有痛點(diǎn),也就少了動(dòng)力。這一章就來(lái)聊聊這塊,對(duì)于有代碼潔癖的人來(lái)說(shuō),還是很賞心悅目的。
四個(gè)注解
@Provider系列的注解有四個(gè):
- @SelectProvider,被定義用來(lái)提供查詢方法的SQL;
- @UpdateProvider,被定義用來(lái)提供更新方法的SQL;
- @DeleteProvider,被定義用來(lái)提供刪除方法的SQL;
- @InsertProvider,被定義用來(lái)提供保存方法的SQL;
官方例子
public interface UserMapper { // 保存用戶數(shù)據(jù) @InsertProvider(type = SqlProvider.class, method = "insert") void insert(User user); public static class SqlProvider { // 對(duì)應(yīng)@InsertProvider注解里的method,返回對(duì)應(yīng)sql public static String insert() { return "INSERT INTO users (id, name) VALUES(#{id}, #{name})"; } } }
序列圖
此章節(jié)主要源碼都在ProviderSqlSource里。
源碼
這里介紹的源碼不多,主要是兩個(gè)部分
sql拼接;
- 反射執(zhí)行provider方法;
sql拼接
sql拼接,主要依賴AbstractSQL里的靜態(tài)方法,下面講一下update語(yǔ)句。
List<String> sets = new ArrayList<>(); List<String> tables = new ArrayList<>(); List<String> where = new ArrayList<>(); public T UPDATE(String table) { // 指定類型為update sql().statementType = SQLStatement.StatementType.UPDATE; // 設(shè)定表名,tables集合添加元素 sql().tables.add(table); // 返回當(dāng)前sqlBuilder對(duì)象 return getSelf(); } private String updateSQL(SafeAppendable builder) { // 拼接 UPDATE [table_name] sqlClause(builder, "UPDATE", tables, "", "", ""); joins(builder); // 拼接sets集合 sqlClause(builder, "SET", sets, "", "", ", "); // 拼接where條件集合 sqlClause(builder, "WHERE", where, "(", ")", " AND "); // 拼接限定條件 limitingRowsStrategy.appendClause(builder, null, limit); return builder.toString(); }
注意:sql拼接是按照一定順序的,tables -> sets -> where,就算是我們?cè)诖a里,刻意打亂順序,也沒(méi)有影響,比如:
WHERE("md5 = #{md5}"); SET("update_time = NOW()"); UPDATE(tableName);
其實(shí)是可以的,畢竟Provider本質(zhì)上,就是提供了待執(zhí)行的sql預(yù)處理語(yǔ)句??垂俜降睦?,其實(shí)就沒(méi)有使用拼接,在后面的例子里,如果不進(jìn)行參數(shù)判空,可以寫成這樣:
public String updateStatus() { return "UPDATE tb_image SET update_time = NOW(), status = ? WHERE (md5 = ?) AND (status = ?)"; }
反射執(zhí)行provider方法
private String invokeProviderMethod(Object... args) throws Exception { Object targetObject = null; if (!Modifier.isStatic(providerMethod.getModifiers())) { // 如果是非靜態(tài)方法,則需要一個(gè)類實(shí)例 targetObject = providerType.getDeclaredConstructor().newInstance(); } // 反射執(zhí)行@Provider里指定的方法 CharSequence sql = (CharSequence) providerMethod.invoke(targetObject, args); // 返回sql語(yǔ)句 return sql != null ? sql.toString() : null; }
這里返回的sql語(yǔ)句,是基于JDBC預(yù)處理語(yǔ)法的字符串,例如UPDATE tb_image SET update_time = NOW(), status = ? WHERE (md5 = ?) AND (status = ?)
參數(shù)是怎么處理的
CharSequence sql = (CharSequence) providerMethod.invoke(targetObject, args);
這一行代碼里,指定了方法參數(shù)【args】,首先說(shuō)明,provider方法里的參數(shù),都來(lái)自于Mapper里的方法參數(shù)值,從params里獲取對(duì)應(yīng)參數(shù)名稱的值,寫入到args;
private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) { Object[] args = new Object[argumentNames.length]; for (int i = 0; i < args.length; i++) { if (providerContextIndex != null && providerContextIndex == i) { args[i] = providerContext; } else { // 關(guān)鍵就是這一句,從params里獲取對(duì)應(yīng)參數(shù)名稱的值,寫入到args; args[i] = params.get(argumentNames[i]); } } return args; }
方法參數(shù)介紹
- params:Mapper里對(duì)應(yīng)方法的所有參數(shù);
- argumentNames:Provider里方法的參數(shù)名稱;
這是下面例子里的參數(shù)處理結(jié)果
我的例子
provider
注意provider里方法的參數(shù),可以和mapper參數(shù)一致,也可以缺失幾個(gè)。引用mapper的參數(shù),主要是為了進(jìn)行邏輯分支判定。
public class ImageDynamicProvider { /** * 圖片更新 * * @return sql */ public String updateStatus(Integer newStatus, Integer oldStatus) { Table table = ImageInfo.class.getAnnotation(Table.class); String tableName = table.name(); return new SQL() { { UPDATE(tableName); SET("update_time = NOW()"); // 判定是否需要更新?tīng)顟B(tài) if (newStatus != null) { SET("status = #{newStatus}"); } WHERE("md5 = #{md5}"); // 判定是否需要此條件 if (oldStatus != null) { AND().WHERE("status = #{oldStatus}"); } } }.toString(); } }
引用Provider
在Mapper對(duì)應(yīng)的方法上面,根據(jù)具體類型,選擇注解。此處是更新語(yǔ)句,所以使用@UpdateProvider,參數(shù)提供了具體的類和方法,供后續(xù)執(zhí)行反射方法。
/** * 更新?tīng)顟B(tài) * * @param md5 圖片摘要信息 */ @UpdateProvider(value = ImageDynamicProvider.class, method = "updateStatus") int updateStatusByProvider(@Param(value = "md5") String md5, @Param(value = "oldStatus") int oldStatus, @Param(value = "newStatus") int newStatus);
測(cè)試用例
@Test public void provider() { imageInfoMapper.updateStatusByProvider( "6e705a7733ac5gbwopmp02", 50, 199); }
輸出
==> Preparing: UPDATE tb_image SET update_time = NOW(), status = ? WHERE (md5 = ?) AND (status = ?)
==> Parameters: 199(Integer), 6e705a7733ac5gbwopmp02(String), 50(Integer)
<== Updates: 1
小問(wèn)題
如果既有Provider,也有xml方法映射。就是說(shuō)我們定義了@Provider注解,又在xml中寫了mapper方法的映射sql語(yǔ)句,這種場(chǎng)景,Mybatis在啟動(dòng)時(shí)就會(huì)報(bào)錯(cuò)。
nested exception is java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.essay.dao.ImageInfoMapper.updateStatusProvider
到此這篇關(guān)于MyBatis中動(dòng)態(tài)SQL語(yǔ)句@Provider的用法的文章就介紹到這了,更多相關(guān)MyBatis 動(dòng)態(tài)SQL @Provider內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Eclipse中實(shí)現(xiàn)快速替換變量
這篇文章主要介紹了Java Eclipse中實(shí)現(xiàn)快速替換變量,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09RestTemplate未使用線程池問(wèn)題的解決方法
今天給大家?guī)?lái)的是關(guān)于Springboot的相關(guān)知識(shí),文章圍繞著RestTemplate未使用線程池展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Java微信公眾平臺(tái)開發(fā)(5) 文本及圖文消息回復(fù)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開發(fā)第五步,回文本及圖文消息回復(fù)的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Spring boot JPA實(shí)現(xiàn)分頁(yè)和枚舉轉(zhuǎn)換代碼示例
這篇文章主要介紹了Spring boot JPA實(shí)現(xiàn)分頁(yè)和枚舉轉(zhuǎn)換代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09使用eclipse + maven一步步搭建SSM框架教程詳解
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、SpringMVC、MyBatis三個(gè)開源框架整合而成,常作為數(shù)據(jù)源較簡(jiǎn)單的web項(xiàng)目的框架.這篇文章主要介紹了eclipse + maven搭建SSM框架 ,需要的朋友可以參考下2017-11-11Automapper實(shí)現(xiàn)自動(dòng)映射的實(shí)例代碼
這篇文章主要介紹了Automapper實(shí)現(xiàn)自動(dòng)映射的實(shí)例代碼,需要的朋友可以參考下2017-09-09Spring MVC請(qǐng)求參數(shù)的深入解析
這篇文章主要給大家介紹了關(guān)于Spring MVC請(qǐng)求參數(shù)解析的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05