一、前言
繼上一篇mybatis查詢語句的背后,這一篇主要圍繞著mybatis查詢的后期操作,即跟數(shù)據(jù)庫交互的時候。由于本人也是一邊學(xué)習(xí)源碼一邊記錄,內(nèi)容難免有錯誤或不足之處,還望諸位指正,本文只可當(dāng)參考作用。謹(jǐn)記!
二、分析
繼上一篇博文的查詢例子,mybatis在最后的查詢最終會走simpleexecutor類的doquery方法,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@override public <e> list<e> doquery(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql) throws sqlexception { statement stmt = null ; try { configuration configuration = ms.getconfiguration(); // 這里也就是采用了策略模式(個人感覺有點(diǎn)像),實(shí)際的statementhandler為routingstatementhandler statementhandler handler = configuration.newstatementhandler(wrapper, ms, parameter, rowbounds, resulthandler, boundsql); stmt = preparestatement(handler, ms.getstatementlog()); // 雖然是執(zhí)行的routingstatementhandler.query,但返回結(jié)果的還是preparedstatementhandler處理 return handler.query(stmt, resulthandler); } finally { closestatement(stmt); } } private statement preparestatement(statementhandler handler, log statementlog) throws sqlexception { statement stmt; // 使用了代理模式,也可以理解為對connection進(jìn)行了一層包裝,這里的作用就是加了log處理 connection connection = getconnection(statementlog); //進(jìn)行預(yù)編譯,即類似jdbc的 sql,如 select * from user where id=? stmt = handler.prepare(connection, transaction.gettimeout()); // 對執(zhí)行查詢的sql進(jìn)行參數(shù)設(shè)置 handler.parameterize(stmt); return stmt; } |
關(guān)于 handler.prepare的作用這里簡單介紹下,不做代碼分析。
會設(shè)置fetchsize,作用就是一次性從數(shù)據(jù)庫抓取數(shù)據(jù),好像默認(rèn)值是10條,如果每次只抓取一條,則進(jìn)行rs.next的時候,會再次查庫。
如果是insert操作,并且數(shù)據(jù)庫主鍵自增且還設(shè)置了可以返回主鍵,則會還做獲取主鍵的操作。
先從設(shè)置參數(shù)說起,也就是handler.parameterize。先看下源碼,具體位置在defaultparameterhandler類里面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
@override public void setparameters(preparedstatement ps) { errorcontext.instance().activity( "setting parameters" ).object(mappedstatement.getparametermap().getid()); // 獲取配置文件里面的sql參數(shù)信息,如sql為select * from user where id=#{userid,jdbctype=integer} // parametermapping 記錄了參數(shù)名也就是userid,還有記錄了對應(yīng)的jdbc類型,還有對應(yīng)的javatype等等,具體可以debug看下 list<parametermapping> parametermappings = boundsql.getparametermappings(); if (parametermappings != null ) { for ( int i = 0 ; i < parametermappings.size(); i++) { parametermapping parametermapping = parametermappings.get(i); if (parametermapping.getmode() != parametermode.out) { object value; string propertyname = parametermapping.getproperty(); // 如果為true,那么sql參數(shù)中有類似 user.name 格式 if (boundsql.hasadditionalparameter(propertyname)) { // issue #448 ask first for additional params value = boundsql.getadditionalparameter(propertyname); } else if (parameterobject == null ) { value = null ; } else if (typehandlerregistry.hastypehandler(parameterobject.getclass())) { value = parameterobject; } else { // metaobject 類似一個工具類,它里面有一個反射工廠,可以專門解析一個類的信息,如字段的setter/getter/屬性信息,這里不做多余介紹 // 1、下面詳細(xì)介紹 metaobject metaobject = configuration.newmetaobject(parameterobject); value = metaobject.getvalue(propertyname); // 取值 } // 獲取對應(yīng)的typehandler,一般情況不設(shè)置的話,基本都是objecttypehandler typehandler typehandler = parametermapping.gettypehandler(); jdbctype jdbctype = parametermapping.getjdbctype(); if (value == null && jdbctype == null ) { jdbctype = configuration.getjdbctypefornull(); } try { // 進(jìn)行設(shè)值 typehandler.setparameter(ps, i + 1 , value, jdbctype); } catch (typeexception e) { throw new typeexception( "could not set parameters for mapping: " + parametermapping + ". cause: " + e, e); } catch (sqlexception e) { throw new typeexception( "could not set parameters for mapping: " + parametermapping + ". cause: " + e, e); } } } } } |
對于上述代碼中的一部分這里負(fù)責(zé)將parameterobject的里面的值整出來(也就是傳入的參數(shù)),如果參數(shù)是map結(jié)構(gòu),就從map里面取值,如果不是,如單個非javabean參數(shù),則直接取值,如果是單個javabean,則通過metaobject類轉(zhuǎn)換成一個beanwrapper,進(jìn)行取值
這段代碼也就負(fù)責(zé)對預(yù)編譯后的sql設(shè)置參數(shù),這里邏輯主要是圍繞以下步驟進(jìn)行得,
獲取參數(shù)名,獲取參數(shù)值,獲取參數(shù)類型,然后做進(jìn)行設(shè)值操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/** * mybatis數(shù)據(jù)處理有單結(jié)果集和多結(jié)果集處理,一般多結(jié)果集出現(xiàn)存儲過程中,如果存儲過程中寫了兩條select語句,如 * select * from user , select * from classes 這種情況這里不做介紹,因?yàn)楸救擞玫牟欢啵斫獾囊膊皇呛芡笍亍?/code> * 這里不多做介紹,這里只針對簡單映射做一個大概介紹 * */ public list<object> handleresultsets(statement stmt) throws sqlexception { errorcontext.instance().activity( "handling results" ).object(mappedstatement.getid()); // 保存查詢結(jié)果 final list<object> multipleresults = new arraylist<>(); int resultsetcount = 0 ; // 獲取第一條數(shù)據(jù) resultsetwrapper rsw = getfirstresultset(stmt); // 如果不是多結(jié)果集映射,一般resultmaps的大小為1 // resultmap中存儲的有類的字段屬性,數(shù)據(jù)庫字段名稱等信息 list<resultmap> resultmaps = mappedstatement.getresultmaps(); int resultmapcount = resultmaps.size(); // 校驗(yàn)數(shù)據(jù)的正確性 validateresultmapscount(rsw, resultmapcount); while (rsw != null && resultmapcount > resultsetcount) { resultmap resultmap = resultmaps.get(resultsetcount); // 處理結(jié)果集映射 handleresultset(rsw, resultmap, multipleresults, null ); rsw = getnextresultset(stmt); cleanupafterhandlingresultset(); resultsetcount++; } // 處理slect 標(biāo)簽的resultsets屬性,多個用逗號隔開,個人幾乎沒用過,略過 string[] resultsets = mappedstatement.getresultsets(); if (resultsets != null ) { while (rsw != null && resultsetcount < resultsets.length) { resultmapping parentmapping = nextresultmaps.get(resultsets[resultsetcount]); if (parentmapping != null ) { string nestedresultmapid = parentmapping.getnestedresultmapid(); resultmap resultmap = configuration.getresultmap(nestedresultmapid); handleresultset(rsw, resultmap, null , parentmapping); } rsw = getnextresultset(stmt); cleanupafterhandlingresultset(); resultsetcount++; } } return collapsesingleresultlist(multipleresults); } |
以上代碼就是為結(jié)果映射做一個鋪墊,重點(diǎn)是在hanleresultset方法里,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
private void handleresultset(resultsetwrapper rsw, resultmap resultmap, list<object> multipleresults, resultmapping parentmapping) throws sqlexception { try { // 針對簡單映射,parentmapping是為null的 if (parentmapping != null ) { handlerowvalues(rsw, resultmap, null , rowbounds. default , parentmapping); } else { // 默認(rèn)使用defaultresulthandler,如需使用自定義的,則可在傳參加入resulthandler接口實(shí)現(xiàn)類 if (resulthandler == null ) { defaultresulthandler defaultresulthandler = new defaultresulthandler(objectfactory); // 處理結(jié)果,結(jié)果存在resulthandler里 handlerowvalues(rsw, resultmap, defaultresulthandler, rowbounds, null ); multipleresults.add(defaultresulthandler.getresultlist()); } else { handlerowvalues(rsw, resultmap, resulthandler, rowbounds, null ); } } } finally { // issue #228 (close resultsets) closeresultset(rsw.getresultset()); } } |
1
2
3
4
5
6
7
8
9
10
|
public void handlerowvalues(resultsetwrapper rsw, resultmap resultmap, resulthandler<?> resulthandler, rowbounds rowbounds, resultmapping parentmapping) throws sqlexception { // 處理有嵌套映射的情況 if (resultmap.hasnestedresultmaps()) { ensurenorowbounds(); checkresulthandler(); handlerowvaluesfornestedresultmap(rsw, resultmap, resulthandler, rowbounds, parentmapping); } else { //沒有嵌套映射 handlerowvaluesforsimpleresultmap(rsw, resultmap, resulthandler, rowbounds, parentmapping); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private void handlerowvaluesforsimpleresultmap(resultsetwrapper rsw, resultmap resultmap, resulthandler<?> resulthandler, rowbounds rowbounds, resultmapping parentmapping) throws sqlexception { defaultresultcontext<object> resultcontext = new defaultresultcontext<>(); resultset resultset = rsw.getresultset(); // 跳過多少行,到達(dá)指定記錄位置,如在傳參的時候傳入了rowbounds,則會根據(jù)該類的offset值跳到指定記錄位置 skiprows(resultset, rowbounds); // shouldprocessmorerows 用來檢測是否能繼續(xù)對后續(xù)的結(jié)果進(jìn)行映射 while (shouldprocessmorerows(resultcontext, rowbounds) && !resultset.isclosed() && resultset.next()) { //用來處理resultmap節(jié)點(diǎn)中配置了discriminator節(jié)點(diǎn),這里忽略掉 resultmap discriminatedresultmap = resolvediscriminatedresultmap(resultset, resultmap, null ); // 得到的結(jié)果就是sql執(zhí)行后的一行記錄,如返回user對象信息,則rowvalue就代表一個user實(shí)例,里面已經(jīng)有值了 object rowvalue = getrowvalue(rsw, discriminatedresultmap, null ); //保存數(shù)據(jù) storeobject(resulthandler, resultcontext, rowvalue, parentmapping, resultset); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
private object getrowvalue(resultsetwrapper rsw, resultmap resultmap, string columnprefix) throws sqlexception { final resultloadermap lazyloader = new resultloadermap(); // 創(chuàng)建對象,可以理解為對resultmap節(jié)點(diǎn)的type屬性值,進(jìn)行了反射處理,得到了一個對象,但屬性值都是默認(rèn)值。 object rowvalue = createresultobject(rsw, resultmap, lazyloader, columnprefix); if (rowvalue != null && !hastypehandlerforresultobject(rsw, resultmap.gettype())) { final metaobject metaobject = configuration.newmetaobject(rowvalue); boolean foundvalues = this .useconstructormappings; //是否需要自動映射,有三種映射,分別為none,partial,full,默認(rèn)第二種,處理非嵌套映射,可通過automappingbehavior 配置 if (shouldapplyautomaticmappings(resultmap, false )) { // 映射resultmap中未明確指定的列,如類中含有username屬性,但是resultmap中沒配置,則通過這個進(jìn)行數(shù)據(jù)映射,還是可以查詢到結(jié)果 foundvalues = applyautomaticmappings(rsw, resultmap, metaobject, columnprefix) || foundvalues; } // 處理resultmap中指定的列 foundvalues = applypropertymappings(rsw, resultmap, metaobject, lazyloader, columnprefix) || foundvalues; foundvalues = lazyloader.size() > 0 || foundvalues; // 如果沒查詢到結(jié)果,但配置可返回空對象(指的是沒有設(shè)置屬性值得對象),則返回空對象,否則返回null rowvalue = foundvalues || configuration.isreturninstanceforemptyrow() ? rowvalue : null ; } return rowvalue; } |
這里只介紹resultmap中有明確指定的列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
private boolean applypropertymappings(resultsetwrapper rsw, resultmap resultmap, metaobject metaobject, resultloadermap lazyloader, string columnprefix) throws sqlexception { // 獲取數(shù)據(jù)字段名 final list<string> mappedcolumnnames = rsw.getmappedcolumnnames(resultmap, columnprefix); boolean foundvalues = false ; // 獲取的數(shù)據(jù)就是resultmap節(jié)點(diǎn)中配置的result節(jié)點(diǎn),有多個result節(jié)點(diǎn),這個集合大小就是多少 // 里面存儲的是屬性名/字段名等信息 final list<resultmapping> propertymappings = resultmap.getpropertyresultmappings(); for (resultmapping propertymapping : propertymappings) { string column = prependprefix(propertymapping.getcolumn(), columnprefix); // 是否有嵌套映射 if (propertymapping.getnestedresultmapid() != null ) { // the user added a column attribute to a nested result map, ignore it column = null ; } // 針對1來說一般常與嵌套查詢配合使用 // 2 判斷屬性基本映射 // 3 多結(jié)果集的一個處理 if (propertymapping.iscompositeresult() // 1 || (column != null && mappedcolumnnames.contains(column.touppercase(locale.english))) // 2 || propertymapping.getresultset() != null ) { // 3 // 獲取當(dāng)前column字段對于的值,有用到typehandler來進(jìn)行參數(shù)的一個轉(zhuǎn)換 object value = getpropertymappingvalue(rsw.getresultset(), metaobject, propertymapping, lazyloader, columnprefix); //獲取類的屬性字段名 final string property = propertymapping.getproperty(); if (property == null ) { continue ; } else if (value == deferred) { // 類似占位符。處理懶加載數(shù)據(jù) foundvalues = true ; continue ; } if (value != null ) { foundvalues = true ; } if (value != null || (configuration.iscallsettersonnulls() && !metaobject.getsettertype(property).isprimitive())) { // 進(jìn)行設(shè)置屬性值 metaobject.setvalue(property, value); } } } return foundvalues; } |
或許有人奇怪為啥沒看到查詢的對象有set操作,值就到了對象里面去了,這里全是metaobject給你操作了,具體的,大家可以自行了解這個類,只能說這個類的功能很強(qiáng)大。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://www.cnblogs.com/qm-article/p/10588627.html