全面解析@InsertProvider執(zhí)行原理
@InsertProvider執(zhí)行原理
1.首先要拼接處insert語(yǔ)句
其中包含入?yún)?,與數(shù)據(jù)庫(kù)表字段的映射字段。
在執(zhí)行Provider類里面的動(dòng)態(tài)插入sql的時(shí)候,程序會(huì)調(diào)用 AbstractSQL這個(gè)抽象類,執(zhí)行里面的兩個(gè)拼接字符串的方法
public T INSERT_INTO(String tableName) { this.sql().statementType = AbstractSQL.SQLStatement.StatementType.INSERT; this.sql().tables.add(tableName); return this.getSelf(); } public T VALUES(String columns, String values) { this.sql().columns.add(columns); this.sql().values.add(values); return this.getSelf(); }
這個(gè)抽象class里面有一個(gè)私有的靜態(tài)類SQLStatement和私有靜態(tài)SalfAppendable類。
SQLStatement類里面有一個(gè)共有靜態(tài)枚舉類,里面有它的一個(gè)私有構(gòu)造函數(shù),枚舉被設(shè)計(jì)成是單例模式,即枚舉類型會(huì)由JVM在加載的時(shí)候,實(shí)例化枚舉對(duì)象,你在枚舉類中定義了多少個(gè)就會(huì)實(shí)例化多少個(gè),JVM為了保證每一個(gè)枚舉類元素的唯一實(shí)例,是不會(huì)允許外部進(jìn)行new的,所以會(huì)把構(gòu)造函數(shù)設(shè)計(jì)成private,防止用戶生成實(shí)例,破壞唯一性。
枚舉類里面含有增刪查改四種。私有的靜態(tài)類SQLStatement有sql語(yǔ)句的各種關(guān)鍵字list,distinct是boolean類型,例如:
List<String> sets = new ArrayList();
子類ArrayList實(shí)例化List,因?yàn)長(zhǎng)ist是一個(gè)接口,所以只能依靠其“子類”(在這里是List的實(shí)現(xiàn)類)來(lái)進(jìn)行實(shí)例化,這里的對(duì)象是List的對(duì)象。
private void sqlClause(AbstractSQL.SafeAppendable builder, String keyword, List<String> parts, String open, String close, String conjunction) { if(!parts.isEmpty()) { if(!builder.isEmpty()) { builder.append("\n"); } builder.append(keyword); builder.append(" "); builder.append(open); String last = "________"; int i = 0; for(int n = parts.size(); i < n; ++i) { String part = (String)parts.get(i); if(i > 0 && !part.equals(") \nAND (") && !part.equals(") \nOR (") && !last.equals(") \nAND (") && !last.equals(") \nOR (")) { builder.append(conjunction); } builder.append(part); last = part; } builder.append(close); } }
上面這個(gè)函數(shù)就是增刪查改通用的sql解析函數(shù)。
下面是增調(diào)用的函數(shù)
private String insertSQL(AbstractSQL.SafeAppendable builder) { this.sqlClause(builder, "INSERT INTO", this.tables, "", "", ""); this.sqlClause(builder, "", this.columns, "(", ")", ", "); this.sqlClause(builder, "VALUES", this.values, "(", ")", ", "); return builder.toString(); }
通過(guò)下面的函數(shù)確定增刪查改對(duì)應(yīng)調(diào)用的函數(shù)
public String sql(Appendable a) { AbstractSQL.SafeAppendable builder = new AbstractSQL.SafeAppendable(a); if(this.statementType == null) { return null; } else { String answer; switch(null.$SwitchMap$org$apache$ibatis$jdbc$AbstractSQL$SQLStatement$StatementType[this.statementType.ordinal()]) { case 1: answer = this.deleteSQL(builder); break; case 2: answer = this.insertSQL(builder); break; case 3: answer = this.selectSQL(builder); break; case 4: answer = this.updateSQL(builder); break; default: answer = null; } return answer; } }
私有靜態(tài)SalfAppendable類是進(jìn)行sql字符串的拼接。
里面有是一個(gè)私有不可以改變的Appendable對(duì)象。
private static class SafeAppendable { private final Appendable a; private boolean empty = true; public SafeAppendable(Appendable a) { this.a = a; } public AbstractSQL.SafeAppendable append(CharSequence s) { try { if(this.empty && s.length() > 0) { this.empty = false; } this.a.append(s); return this; } catch (IOException var3) { throw new RuntimeException(var3); } } public boolean isEmpty() { return this.empty; } }
然后調(diào)用ProviderSqlSource類,接著調(diào)用不可變類MappedStatement類,再調(diào)用BaseStatementHandler類,--->PreparedStatementHandler等。
2.ProviderSqlSource實(shí)現(xiàn)了sqlSource接口
代表從注解中讀取相關(guān)的映射語(yǔ)句的內(nèi)容,它創(chuàng)建的sql會(huì)被傳到數(shù)據(jù)庫(kù)。
根據(jù)SQL語(yǔ)句的類型不同,mybatis提供了多種SqlSource的具體實(shí)現(xiàn):
1)StaticSqlSource
:最終靜態(tài)SQL語(yǔ)句的封裝,其他類型的SqlSource最終都委托給StaticSqlSource。
2)RawSqlSource
:原始靜態(tài)SQL語(yǔ)句的封裝,在加載時(shí)就已經(jīng)確定了SQL語(yǔ)句,沒(méi)有、等動(dòng)態(tài)標(biāo)簽和${} SQL拼接,比動(dòng)態(tài)SQL語(yǔ)句要快,因?yàn)椴恍枰\(yùn)行時(shí)解析SQL節(jié)點(diǎn)。
3)DynamicSqlSource
:動(dòng)態(tài)SQL語(yǔ)句的封裝,在運(yùn)行時(shí)需要根據(jù)參數(shù)處理、等標(biāo)簽或者${} SQL拼接之后才能生成最后要執(zhí)行的靜態(tài)SQL語(yǔ)句。
4)ProviderSqlSource
:當(dāng)SQL語(yǔ)句通過(guò)指定的類和方法獲取時(shí)(使用@XXXProvider注解),需要使用本類,它會(huì)通過(guò)反射調(diào)用相應(yīng)的方法得到SQL語(yǔ)句
關(guān)于@Insert和@InsertProvider注解用法
@Insert和@InsertProvider都是用來(lái)在實(shí)體類的Mapper類里注解保存方法的SQL語(yǔ)句。
不同的是,@Insert是直接配置SQL語(yǔ)句,而@InsertProvider則是通過(guò)SQL工廠類及對(duì)應(yīng)的方法生產(chǎn)SQL語(yǔ)句,這種方法的好處在于,我們可以根據(jù)不同的需求生產(chǎn)出不同的SQL,適用性更好。
1.項(xiàng)目主要結(jié)構(gòu)
(1)項(xiàng)目中的實(shí)體類
(2)每個(gè)實(shí)體類對(duì)應(yīng)的Mapper方法
(3)SQL工廠
2.下面以BlogMapper中的保存Blog實(shí)體方法為例
Blog實(shí)體類屬性:
為了方便說(shuō)明,屬性不設(shè)置過(guò)多,假設(shè)Blog類的屬性有blogId,title,author
(1)@Insert的注解方式
@Insert("insert into blog(blogId,title,author) values(#blogId,#title,#author)") public boolean saveBlog(Blog blog);
說(shuō)明:由于我們不能確定哪些屬性沒(méi)有值,那只能把所有屬性都寫(xiě)上了。
(2)@InsertProvider的注解方式
@InsertProvider(type = SqlFactory.class,method = "insertBlog") public boolean saveBlog(@Param("bean")Blog blog);
說(shuō)明:type指明SQL工廠類,method是工廠類里對(duì)應(yīng)的方法
SqlFactory類代碼:
public class SqlFactory { public String insertBlog(Map<String,Object> para){ Blog blog = (Blog)para.get("bean"); // SQL sql = new SQL(); //SQL語(yǔ)句對(duì)象,所在包:org.apache.ibatis.jdbc.SQL sql.INSERT_INTO("blog"); if(blog.getBlogId() != null){ //判斷blogId屬性是否有值 sql.VALUES("blogId", blog.getBlogId()); } if(blog.getTitle() != null){//判斷title屬性是否有值 sql.VALUES("title", blog.getTitle()); } if(blog.getAuthor() != null){//判斷author屬性是否有值 sql.VALUES("author", blog.getAuthor()); } return sql.toString(); } }
使用@InsertProvider的方式,可以根據(jù)實(shí)體中有值的屬性,進(jìn)行動(dòng)態(tài)的生成插入SQL語(yǔ)句如:
- blogId和title有值:insert into blog(blogId,title) values(v1,v2);
- author和title有值:insert into blog(author,title) values(v1,v2);
總結(jié):其實(shí)也就是說(shuō)因?yàn)閙ybaits的xml中有<if test=""></if>標(biāo)簽來(lái)動(dòng)態(tài)生成sql,但是在程序代碼中沒(méi)有辦法這么做。
那么insertprovider就是充當(dāng)了這樣一個(gè)角色,來(lái)動(dòng)態(tài)的生成sql。
與之類似的還有MyBatis注解的巧妙使用---@InsertProvider,@UpdateProvider,@DeleteProvider和@SelectProvider等等。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
mybatis自動(dòng)生成時(shí)如何設(shè)置不生成Example類詳解
這篇文章主要給大家介紹了關(guān)于mybatis自動(dòng)生成時(shí)如何設(shè)置不生成Example類的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-05-05spring應(yīng)用中多次讀取http post方法中的流遇到的問(wèn)題
這篇文章主要介紹了spring應(yīng)用中多次讀取http post方法中的流,文中給大家列舉處理問(wèn)題描述及解決方法,需要的朋友可以參考下2018-11-11IDEA2023.3.4開(kāi)啟SpringBoot項(xiàng)目的熱部署(圖文)
本文使用的開(kāi)發(fā)工具是idea,使用的是springboot框架開(kāi)發(fā)的項(xiàng)目,配置熱部署,可以提高開(kāi)發(fā)效率,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度
這篇文章主要為大家介紹了java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Java 設(shè)計(jì)模式之責(zé)任鏈模式及異步責(zé)任鏈詳解
顧名思義,責(zé)任鏈模式(Chain of Responsibility Pattern)為請(qǐng)求創(chuàng)建了一個(gè)接收者對(duì)象的鏈。這種模式給予請(qǐng)求的類型,對(duì)請(qǐng)求的發(fā)送者和接收者進(jìn)行解耦。這種類型的設(shè)計(jì)模式屬于行為型模式2021-11-11mybatis修改int型數(shù)據(jù)無(wú)法修改成0的解決
這篇文章主要介紹了mybatis修改int型數(shù)據(jù)無(wú)法修改成0的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Spring Security 自動(dòng)踢掉前一個(gè)登錄用戶的實(shí)現(xiàn)代碼
這篇文章主要介紹了Spring Security 自動(dòng)踢掉前一個(gè)登錄用戶的實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05SpringBoot中Date格式化處理的三種實(shí)現(xiàn)
Spring Boot作為一個(gè)簡(jiǎn)化Spring應(yīng)用開(kāi)發(fā)的框架,提供了多種處理日期格式化的方法,本文主要介紹了SpringBoot中Date格式化處理實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03