全面解析@InsertProvider執(zhí)行原理
@InsertProvider執(zhí)行原理
1.首先要拼接處insert語句
其中包含入?yún)?,與數(shù)據(jù)庫表字段的映射字段。
在執(zhí)行Provider類里面的動態(tài)插入sql的時(shí)候,程序會調(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ì)成是單例模式,即枚舉類型會由JVM在加載的時(shí)候,實(shí)例化枚舉對象,你在枚舉類中定義了多少個(gè)就會實(shí)例化多少個(gè),JVM為了保證每一個(gè)枚舉類元素的唯一實(shí)例,是不會允許外部進(jìn)行new的,所以會把構(gòu)造函數(shù)設(shè)計(jì)成private,防止用戶生成實(shí)例,破壞唯一性。
枚舉類里面含有增刪查改四種。私有的靜態(tài)類SQLStatement有sql語句的各種關(guān)鍵字list,distinct是boolean類型,例如:
List<String> sets = new ArrayList();
子類ArrayList實(shí)例化List,因?yàn)長ist是一個(gè)接口,所以只能依靠其“子類”(在這里是List的實(shí)現(xiàn)類)來進(jìn)行實(shí)例化,這里的對象是List的對象。
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();
}通過下面的函數(shù)確定增刪查改對應(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對象。
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)的映射語句的內(nèi)容,它創(chuàng)建的sql會被傳到數(shù)據(jù)庫。
根據(jù)SQL語句的類型不同,mybatis提供了多種SqlSource的具體實(shí)現(xiàn):
1)StaticSqlSource:最終靜態(tài)SQL語句的封裝,其他類型的SqlSource最終都委托給StaticSqlSource。
2)RawSqlSource:原始靜態(tài)SQL語句的封裝,在加載時(shí)就已經(jīng)確定了SQL語句,沒有、等動態(tài)標(biāo)簽和${} SQL拼接,比動態(tài)SQL語句要快,因?yàn)椴恍枰\(yùn)行時(shí)解析SQL節(jié)點(diǎn)。
3)DynamicSqlSource:動態(tài)SQL語句的封裝,在運(yùn)行時(shí)需要根據(jù)參數(shù)處理、等標(biāo)簽或者${} SQL拼接之后才能生成最后要執(zhí)行的靜態(tài)SQL語句。
4)ProviderSqlSource:當(dāng)SQL語句通過指定的類和方法獲取時(shí)(使用@XXXProvider注解),需要使用本類,它會通過反射調(diào)用相應(yīng)的方法得到SQL語句
關(guān)于@Insert和@InsertProvider注解用法
@Insert和@InsertProvider都是用來在實(shí)體類的Mapper類里注解保存方法的SQL語句。
不同的是,@Insert是直接配置SQL語句,而@InsertProvider則是通過SQL工廠類及對應(yīng)的方法生產(chǎn)SQL語句,這種方法的好處在于,我們可以根據(jù)不同的需求生產(chǎn)出不同的SQL,適用性更好。
1.項(xiàng)目主要結(jié)構(gòu)
(1)項(xiàng)目中的實(shí)體類
(2)每個(gè)實(shí)體類對應(yīng)的Mapper方法
(3)SQL工廠

2.下面以BlogMapper中的保存Blog實(shí)體方法為例
Blog實(shí)體類屬性:
為了方便說明,屬性不設(shè)置過多,假設(shè)Blog類的屬性有blogId,title,author
(1)@Insert的注解方式
@Insert("insert into blog(blogId,title,author) values(#blogId,#title,#author)")
public boolean saveBlog(Blog blog);說明:由于我們不能確定哪些屬性沒有值,那只能把所有屬性都寫上了。
(2)@InsertProvider的注解方式
@InsertProvider(type = SqlFactory.class,method = "insertBlog")
public boolean saveBlog(@Param("bean")Blog blog);說明:type指明SQL工廠類,method是工廠類里對應(yīng)的方法
SqlFactory類代碼:
public class SqlFactory {
public String insertBlog(Map<String,Object> para){
Blog blog = (Blog)para.get("bean"); //
SQL sql = new SQL(); //SQL語句對象,所在包: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)行動態(tài)的生成插入SQL語句如:
- blogId和title有值:insert into blog(blogId,title) values(v1,v2);
- author和title有值:insert into blog(author,title) values(v1,v2);
總結(jié):其實(shí)也就是說因?yàn)閙ybaits的xml中有<if test=""></if>標(biāo)簽來動態(tài)生成sql,但是在程序代碼中沒有辦法這么做。
那么insertprovider就是充當(dāng)了這樣一個(gè)角色,來動態(tài)的生成sql。
與之類似的還有MyBatis注解的巧妙使用---@InsertProvider,@UpdateProvider,@DeleteProvider和@SelectProvider等等。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JavaWeb項(xiàng)目實(shí)現(xiàn)文件上傳動態(tài)顯示進(jìn)度實(shí)例
本篇文章主要介紹了JavaWeb項(xiàng)目實(shí)現(xiàn)文件上傳動態(tài)顯示進(jìn)度實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-04-04
Spring中事務(wù)管理方案和事務(wù)管理器及事務(wù)控制的API詳解
這篇文章主要介紹了Spring中事務(wù)管理方案和事務(wù)管理器及事務(wù)控制的API詳解,事務(wù)管理是指對事務(wù)進(jìn)行管理和控制,以確保事務(wù)的正確性和完整性,事務(wù)管理的作用是保證數(shù)據(jù)庫的數(shù)據(jù)操作的一致性和可靠性,需要的朋友可以參考下2023-08-08
OpenFeign服務(wù)接口調(diào)用的過程詳解
Feign是一個(gè)聲明式WebService客戶端。使用Feign能讓編寫Web?Service客戶端更加簡單。它的使用方法是定義一個(gè)服務(wù)接口然后在上面添加注解,這篇文章主要介紹了OpenFeign服務(wù)接口調(diào)用,需要的朋友可以參考下2022-10-10
java中File與MultipartFile互轉(zhuǎn)代碼示例
在Java開發(fā)中,當(dāng)需要將本地File對象轉(zhuǎn)換為MultipartFile對象以處理文件上傳時(shí),可以通過實(shí)現(xiàn)MultipartFile接口或使用CommonsMultipartFile類來實(shí)現(xiàn),本文提供了詳細(xì)的轉(zhuǎn)換方法和代碼示例,需要的朋友可以參考下2024-10-10
Java?lambda表達(dá)式與泛型整理總結(jié)
Lambda?表達(dá)式(lambda?expression)是一個(gè)匿名函數(shù),Lambda表達(dá)式基于數(shù)學(xué)中的λ演算得名。泛型編程,故如其名,是一個(gè)泛化的編程方式。其實(shí)現(xiàn)原理為程序員編寫一個(gè)函數(shù)/類的代碼示例,讓編譯器去填補(bǔ)出不同的函數(shù)實(shí)現(xiàn)2022-07-07

