1、創建項目maven,方便依賴下載。使用的jar如下:
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
|
<dependencies> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> <version> 5.0 . 8 .release</version> </dependency> <dependency> <groupid>org.mybatis</groupid> <artifactid>mybatis-spring</artifactid> <version> 2.0 . 0 </version> </dependency> <dependency> <groupid>org.mybatis</groupid> <artifactid>mybatis</artifactid> <version> 3.4 . 6 </version> </dependency> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <version> 5.1 . 34 </version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-jdbc</artifactid> <version> 5.0 . 8 .release</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-core</artifactid> <version> 5.0 . 8 .release</version> </dependency> </dependencies> |
2、創建包com >config、dao、service、test
3、使用spring創建appconfig文件,創建bean>sqlsessionfactorybean、datasourcebean
1、spring注解@configuration,說明是配置層
2、注冊掃描映射
3、注冊包掃描器
4、創建sqlsessionfactorybean
5、創建數據源bean
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
|
package com.config; import org.mybatis.spring.sqlsessionfactorybean; import org.mybatis.spring.annotation.mapperscan; import org.springframework.beans.factory.annotation.autowired; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.componentscan; import org.springframework.context.annotation.configuration; import org.springframework.jdbc.datasource.drivermanagerdatasource; import javax.sql.datasource; @configuration @mapperscan ( "com.dao" ) //注解 與xml<mybatis:scan base-package="org.mybatis.spring.sample.mapper" /> //注冊包中遞歸搜索映射器 @componentscan ( "com" ) //注冊bean public class appconfig { @bean @autowired public sqlsessionfactorybean sqlsessionfactorybean(datasource datasource ){ sqlsessionfactorybean sqlsessionfactorybean = new sqlsessionfactorybean(); sqlsessionfactorybean.setdatasource(datasource); return sqlsessionfactorybean; } @bean public datasource getdatasource(){ drivermanagerdatasource datasource = new drivermanagerdatasource(); datasource.setdriverclassname( "com.mysql.jdbc.driver" ); datasource.setusername( "root" ); datasource.setpassword( "110226wjwj" ); datasource.seturl( "jdbc:mysql://localhost:3306/mybatis_wjw?useunicode=true&characterencoding=utf-8&usessl=false" ); return datasource; } } |
4、創建dao接口
1、創建query方法并使用注解@select(mybatis提供,mybatis-spring官網有相關解釋)
1
2
3
4
5
6
7
8
|
package com.dao; import org.apache.ibatis.annotations.select; import java.util.list; import java.util.map; public interface userdao { @select ( "select * from t_user where tid =3" ) public list<map> query(); } |
5、創建服務層
1、注解服務層
2、引入daobean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.service; import com.dao.userdao; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.service; //spring注解中service與component意思差不多,區別在于component是中立注解,而service是業務邏輯層的注解 //@component @service public class userservice { @autowired userdao userdao; public void query(){ system.out.println(userdao.query()); } } |
6、測試
1、創建application
2、創建service
1
2
3
4
5
6
7
8
9
10
11
|
package com.test; import com.config.appconfig; import com.service.userservice; import org.springframework.context.annotation.annotationconfigapplicationcontext; public class maintest { public static void main(string args[]){ annotationconfigapplicationcontext acc = new annotationconfigapplicationcontext(appconfig. class ); userservice us = acc.getbean(userservice. class ); us.query(); } } |
jar包之家:https://mvnrepository.com/artifact
使用spring依賴注入mapper 根據官網提示需要
<mybatis:scan base-package="org.mybatis.spring.sample.mapper" />==@mapperscan(“需要注入的包”)
mybatis缺點:使用xml方式與dao開發結合出現嚴重的臃腫現象,需要維護很多sql語句。
測試的時候出現這個問題。(版本導致,我直接將最新[mybatis-spring]的導入進來就沒問題了)
上面的問題解決后有出現這個問題,此問題出現的原因是java compiler改成8就ok了
測試結果:
重點:
mybatis運行原理
我們可以看到,再測試類中用的是userservice對象調用dao接口中的query,但是mybatis是如何實現將接口轉換成對象的呢? 答案:動態代理 ,我們常用的代理(proxy)一共有兩種:cglib和jdk兩用代理模式
無論哪一種最終都是使用反射機制進行代理。詳情自己查看度娘(哈哈@0@)
mybatis源碼解析
defaultsqlsession下如何實現返回一條數據的:底層調用的是selectlist方法,對返回的結果集進行數量判斷如果==1則直接放回,>1直接拋出toomanyresultsexception(感覺很傻,有些妄自菲薄!)
1
2
3
4
5
6
7
8
9
10
|
public <t> t selectone(string statement, object parameter) { list<t> list = this .selectlist(statement, parameter); if (list.size() == 1 ) { return list.get( 0 ); } else if (list.size() > 1 ) { throw new toomanyresultsexception( "expected one result (or null) to be returned by selectone(), but found: " + list.size()); } else { return null ; } } |
下面我們繼續看selectlist是如何實現的
1
2
3
4
5
6
7
8
9
10
11
12
|
public <e> list<e> selectlist(string statement, object parameter, rowbounds rowbounds) { list var5; try { mappedstatement ms = this .configuration.getmappedstatement(statement); var5 = this .executor.query(ms, this .wrapcollection(parameter), rowbounds, executor.no_result_handler); } catch (exception var9) { throw exceptionfactory.wrapexception( "error querying database. cause: " + var9, var9); } finally { errorcontext.instance().reset(); } return var5; } |
我們來看看這個defaultsqlsession.configuration.getmappedstatement方法具體是什么
結論:再啟動項目的時候,mybatis會進行初始化,這個初始化就是將我們的"包+類+方法名"作為key 和 sql語句作為value 的鍵值對形式賦給下面map類型的mappedstatements
protected final map<string, mappedstatement> mappedstatements;
這也是為什么我們使用xml方式一定要將方法名字與id對應上才能使用,如果對應不上再進行id傳值的時候找不到對應的key。
繼續往下分析:
已發帖子詢問大神具體是什么原因導致不進入124行,等大佬們回答后我將公布結果。直接看看executor是什么鬼
是一個接口,https://blog.csdn.net/ykzhen2015/article/details/50315027
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
|
protected set<beandefinitionholder> doscan(string... basepackages) { assert .notempty(basepackages, "at least one base package must be specified" ); set<beandefinitionholder> beandefinitions = new linkedhashset(); string[] var3 = basepackages; int var4 = basepackages.length; for ( int var5 = 0 ; var5 < var4; ++var5) { string basepackage = var3[var5]; set<beandefinition> candidates = this .findcandidatecomponents(basepackage); iterator var8 = candidates.iterator(); while (var8.hasnext()) { beandefinition candidate = (beandefinition)var8.next(); scopemetadata scopemetadata = this .scopemetadataresolver.resolvescopemetadata(candidate); candidate.setscope(scopemetadata.getscopename()); string beanname = this .beannamegenerator.generatebeanname(candidate, this .registry); if (candidate instanceof abstractbeandefinition) { this .postprocessbeandefinition((abstractbeandefinition)candidate, beanname); } if (candidate instanceof annotatedbeandefinition) { annotationconfigutils.processcommondefinitionannotations((annotatedbeandefinition)candidate); } if ( this .checkcandidate(beanname, candidate)) { beandefinitionholder definitionholder = new beandefinitionholder(candidate, beanname); definitionholder = annotationconfigutils.applyscopedproxymode(scopemetadata, definitionholder, this .registry); beandefinitions.add(definitionholder); this .registerbeandefinition(definitionholder, this .registry); } } } return beandefinitions; } |
關于mybatis中的executor與一級緩存的關系:
這里引出一個經典問題,關于mybatis一級緩存問題,這個緩存存儲在session中,當sql關閉的時候就會自動銷毀,涉及到mybatis中的session生命周期問題
為什么mybatis與spring結合后一級緩存會失效?以為sqlsession是由sqlsessionfactorybean生成,二這個sqlsessionfactorybean是由spring管理,也就是此時的session是由spring進行管理的并不是mybatis管理,所以此時session緩存會失效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public interface executor { resulthandler no_result_handler = null ; int update(mappedstatement var1, object var2) throws sqlexception; <e> list<e> query(mappedstatement var1, object var2, rowbounds var3, resulthandler var4, cachekey var5, boundsql var6) throws sqlexception; <e> list<e> query(mappedstatement var1, object var2, rowbounds var3, resulthandler var4) throws sqlexception; <e> cursor<e> querycursor(mappedstatement var1, object var2, rowbounds var3) throws sqlexception; list<batchresult> flushstatements() throws sqlexception; void commit( boolean var1) throws sqlexception; void rollback( boolean var1) throws sqlexception; cachekey createcachekey(mappedstatement var1, object var2, rowbounds var3, boundsql var4); boolean iscached(mappedstatement var1, cachekey var2); void clearlocalcache(); void deferload(mappedstatement var1, metaobject var2, string var3, cachekey var4, class <?> var5); transaction gettransaction(); void close( boolean var1); boolean isclosed(); void setexecutorwrapper(executor var1); } |
mybatis-spring依賴中mapperscannerregistrar的作用:registerbeandefinitions方法中的doscan()此方法是將mapper掃描到初始化中。原理就是獲取到項目路徑找到對應的classes路徑,獲取了com.dao包名,將獲取的路徑+包名轉成文件夾,循環獲取文件夾下面的文件(userdao.class),然后使用將userdao拿到。此時包名+類名都拿到后使用class.forname(包名+類名)反射出對象,進行bean注冊
總結運行原理:
初始化信息(數據庫連接信息,掃描mapper包中的class用于創建bean對象,spring中的類applicationfactory用于創建變對象、mapper中的xml的id與sql放到mapperstatement對象中)
其中對于掃描mapper包中的class路徑+參數basepackages轉成文件夾,然后循環找到所有的類名,使用.......(請看mapperscannerregistrar引出的doscan方法)
執行過程:根據sqlsessionfactorybean創建出sqlsession,調用selectlist方法,之后根據參數(namespacename+id)作為key找到mapperstatement對象中存儲的value獲取到sql語句,再有executor(mybatis默認使用 cacheexecutor)執行sql語句查詢出結果集
原文鏈接:https://www.cnblogs.com/gnwzj/p/10680940.html