久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Mybatis框架復(fù)習(xí)大綱【面試+提高】

 Java幫幫 2020-01-02


Mybatis框架復(fù)習(xí)大綱【面試+提高】

1.MyBatis面試題匯總

1.1 JDBC編程有哪些不足之處,MyBatis是如何解決這些問(wèn)題的?

① 數(shù)據(jù)庫(kù)鏈接創(chuàng)建,、釋放頻繁造成系統(tǒng)資源浪費(fèi)從而影響系統(tǒng)性能,如果使用數(shù)據(jù)庫(kù)鏈接池可解決此問(wèn)題,。

解決:在SqlMapConfig.xml中配置數(shù)據(jù)鏈接池,,使用連接池管理數(shù)據(jù)庫(kù)鏈接。

② Sql語(yǔ)句寫(xiě)在代碼中造成代碼不易維護(hù),,實(shí)際應(yīng)用sql變化的可能較大,,sql變動(dòng)需要改變java代碼。

解決:將Sql語(yǔ)句配置在XXXXmapper.xml文件中與java代碼分離,。

③ 向sql語(yǔ)句傳參數(shù)麻煩,,因?yàn)閟ql語(yǔ)句的where條件不一定,可能多也可能少,,占位符需要和參數(shù)一一對(duì)應(yīng),。 

解決: Mybatis自動(dòng)將java對(duì)象映射至sql語(yǔ)句。

④ 對(duì)結(jié)果集解析麻煩,,sql變化導(dǎo)致解析代碼變化,,且解析前需要遍歷,如果能將數(shù)據(jù)庫(kù)記錄封裝成pojo對(duì)象解析比較方便,。

解決:Mybatis自動(dòng)將sql執(zhí)行結(jié)果映射至java對(duì)象,。


1.2 MyBatis編程步驟是什么樣的?

① 創(chuàng)建SqlSessionFactory

② 通過(guò)SqlSessionFactory創(chuàng)建SqlSession

③ 通過(guò)sqlsession執(zhí)行數(shù)據(jù)庫(kù)操作

④ 調(diào)用session.commit()提交事務(wù)

⑤ 調(diào)用session.close()關(guān)閉會(huì)話


1.3 MyBatis與Hibernate有哪些不同,?

1.Mybatis和hibernate不同,,它不完全是一個(gè)ORM框架,因?yàn)镸yBatis需要程序員自己編寫(xiě)Sql語(yǔ)句,。mybatis可以通過(guò)XML或注解方式靈活配置要運(yùn)行的sql語(yǔ)句,,并將java對(duì)象和sql語(yǔ)句映射生成最終執(zhí)行的sql,,最后將sql執(zhí)行的結(jié)果再映射生成java對(duì)象。

2.Mybatis學(xué)習(xí)門(mén)檻低,,簡(jiǎn)單易學(xué),,程序員直接編寫(xiě)原生態(tài)sql,可嚴(yán)格控制sql執(zhí)行性能,,靈活度高,,非常適合對(duì)關(guān)系數(shù)據(jù)模型要求不高的軟件開(kāi)發(fā),例如互聯(lián)網(wǎng)軟件,、企業(yè)運(yùn)營(yíng)類(lèi)軟件等,,因?yàn)檫@類(lèi)軟件需求變化頻繁,一但需求變化要求成果輸出迅速,。但是靈活的前提是mybatis無(wú)法做到數(shù)據(jù)庫(kù)無(wú)關(guān)性,,如果需要實(shí)現(xiàn)支持多種數(shù)據(jù)庫(kù)的軟件則需要自定義多套sql映射文件,工作量大,。

3.Hibernate對(duì)象/關(guān)系映射能力強(qiáng),,數(shù)據(jù)庫(kù)無(wú)關(guān)性好,對(duì)于關(guān)系模型要求高的軟件(例如需求固定的定制化軟件)如果用hibernate開(kāi)發(fā)可以節(jié)省很多代碼,,提高效率,。但是Hibernate的學(xué)習(xí)門(mén)檻高,要精通門(mén)檻更高,,而且怎么設(shè)計(jì)O/R映射,,在性能和對(duì)象模型之間如何權(quán)衡,以及怎樣用好Hibernate需要具有很強(qiáng)的經(jīng)驗(yàn)和能力才行,。

總之,,按照用戶(hù)的需求在有限的資源環(huán)境下只要能做出維護(hù)性、擴(kuò)展性良好的軟件架構(gòu)都是好架構(gòu),,所以框架只有適合才是最好,。 


1.4 使用MyBatis的mapper接口調(diào)用時(shí)有哪些要求?

①  Mapper接口方法名和mapper.xml中定義的每個(gè)sql的id相同

②  Mapper接口方法的輸入?yún)?shù)類(lèi)型和mapper.xml中定義的每個(gè)sql 的parameterType的類(lèi)型相同

③  Mapper接口方法的輸出參數(shù)類(lèi)型和mapper.xml中定義的每個(gè)sql的resultType的類(lèi)型相同

④  Mapper.xml文件中的namespace即是mapper接口的類(lèi)路徑,。


1.5 SqlMapConfig.xml中配置有哪些內(nèi)容?

SqlMapConfig.xml中配置的內(nèi)容和順序如下: 

properties(屬性)

settings(配置)

typeAliases(類(lèi)型別名)

typeHandlers(類(lèi)型處理器)

objectFactory(對(duì)象工廠)

plugins(插件)

environments(環(huán)境集合屬性對(duì)象)

environment(環(huán)境子屬性對(duì)象)

transactionManager(事務(wù)管理)

dataSource(數(shù)據(jù)源)

mappers(映射器)


1.6簡(jiǎn)單的說(shuō)一下MyBatis的一級(jí)緩存和二級(jí)緩存,?

Mybatis首先去緩存中查詢(xún)結(jié)果集,,如果沒(méi)有則查詢(xún)數(shù)據(jù)庫(kù),,如果有則從緩存取出返回結(jié)果集就不走數(shù)據(jù)庫(kù),。Mybatis內(nèi)部存儲(chǔ)緩存使用一個(gè)HashMap,key為hashCode+sqlId+Sql語(yǔ)句,。value為從查詢(xún)出來(lái)映射生成的java對(duì)象

Mybatis的二級(jí)緩存即查詢(xún)緩存,,它的作用域是一個(gè)mapper的namespace,,即在同一個(gè)namespace中查詢(xún)sql可以從緩存中獲取數(shù)據(jù),。二級(jí)緩存是可以跨SqlSession的,。


1.7Mapper編寫(xiě)有哪幾種方式,?

①接口實(shí)現(xiàn)類(lèi)繼承SqlSessionDaoSupport

使用此種方法需要編寫(xiě)mapper接口,,mapper接口實(shí)現(xiàn)類(lèi),、mapper.xml文件

1).在sqlMapConfig.xml中配置mapper.xml的位置

2).定義mapper接口

3).實(shí)現(xiàn)類(lèi)集成SqlSessionDaoSupport

mapper方法中可以this.getSqlSession()進(jìn)行數(shù)據(jù)增刪改查。

4).spring 配置

②使用org.mybatis.spring.mapper.MapperFactoryBean

1).在sqlMapConfig.xml中配置mapper.xml的位置

如果mapper.xml和mappre接口的名稱(chēng)相同且在同一個(gè)目錄,,這里可以不用配置

2).定義mapper接口

注意:

1,、mapper.xml中的namespace為mapper接口的地址

2、mapper接口中的方法名和mapper.xml中的定義的statement的id保持一致

3,、 Spring中定義

③使用mapper掃描器

1).mapper.xml文件編寫(xiě),,

注意:

mapper.xml中的namespace為mapper接口的地址

mapper接口中的方法名和mapper.xml中的定義的statement的id保持一致

如果將mapper.xml和mapper接口的名稱(chēng)保持一致則不用在sqlMapConfig.xml中進(jìn)行配置 

2).定義mapper接口

注意mapper.xml的文件名和mapper的接口名稱(chēng)保持一致,且放在同一個(gè)目錄

3).配置mapper掃描器

4).使用掃描器后從spring容器中獲取mapper的實(shí)現(xiàn)對(duì)象

掃描器將接口通過(guò)代理方法生成實(shí)現(xiàn)對(duì)象,,要spring容器中自動(dòng)注冊(cè),,名稱(chēng)為mapper 接口的名稱(chēng)。


2. 建立工程時(shí)選擇建立java工程還是web工程,?

當(dāng)建立的工程,需要用到request/response時(shí),需要建立web工程,否則Java工程即可。


3. MyBatis介紹

MyBatis本是apache公司一個(gè)名叫iBatis的開(kāi)源項(xiàng)目,在2010年Apache將其轉(zhuǎn)移給了Google公司,從apache software foundation 遷移到了google code,并且改名為MyBatis,后來(lái)在2013年11月又被Google將其放到了Github上,。

MyBatis是一個(gè)優(yōu)秀的持久層框架,它對(duì)jdbc操作數(shù)據(jù)庫(kù)的過(guò)程進(jìn)行了封裝,開(kāi)發(fā)者只需要關(guān)注SQL本身,而不需要浪費(fèi)精力去處理,例如:注冊(cè)驅(qū)動(dòng),創(chuàng)建connection,創(chuàng)建statement,手動(dòng)設(shè)置參數(shù),結(jié)果集檢索等Jdbc繁雜的過(guò)程代碼,。

MyBatis通過(guò)xml或注解的方式將要執(zhí)行的各種statement(statement、preparedStatemnt,、CallableStatement)配置起來(lái),,并通過(guò)java對(duì)象和statement中的sql進(jìn)行映射生成最終執(zhí)行的sql語(yǔ)句,最后由mybatis框架執(zhí)行sql并將結(jié)果映射成java對(duì)象并返回,。

1).和jdbc比較: 
mybatis抽離出數(shù)據(jù)庫(kù)的連接,關(guān)閉的操作.抽離了sql語(yǔ)句,并且可以自動(dòng)的進(jìn)行參數(shù)的設(shè)置,封裝結(jié)果集.

2).和hibernate比較: 
- 性能:mybatis較hibernate高 
- sql靈活性:mybatis較hibernate高 
- 配置文件:mybatis較hibernate多(維護(hù)困難) 
- 數(shù)據(jù)庫(kù)的無(wú)關(guān)性:mybatis較hibernate低


4. jdbc編程步驟回顧

  1. 注冊(cè)數(shù)據(jù)庫(kù)驅(qū)動(dòng) 

  2. 創(chuàng)建并獲取數(shù)據(jù)庫(kù)鏈接 

  3. 創(chuàng)建jdbc statement對(duì)象 

  4. 設(shè)置sql語(yǔ)句 

  5. 設(shè)置sql語(yǔ)句中的參數(shù)(使用preparedStatement) 

  6. 通過(guò)statement執(zhí)行sql并獲取結(jié)果 

  7. 對(duì)sql執(zhí)行結(jié)果進(jìn)行解析處理, while(resultSet.next) 

  8. 釋放資源(resultSet,preparedStatement,connection)


5. jdbc問(wèn)題總結(jié)

  1. 數(shù)據(jù)庫(kù)連接創(chuàng)建,、釋放頻繁造成系統(tǒng)資源浪費(fèi),從而影響系統(tǒng)性能。如果使用數(shù)據(jù)庫(kù)連接池可解決此問(wèn)題,。

  2. Sql語(yǔ)句在代碼中硬編碼,,造成代碼不易維護(hù),實(shí)際應(yīng)用中sql變化的可能較大,,sql變動(dòng)需要改變java代碼,。

  3. 使用preparedStatement向占位符號(hào)傳參數(shù)存在硬編碼,因?yàn)閟ql語(yǔ)句的where條件不一定,,可能多也可能少,,修改sql還要修改代碼,系統(tǒng)不易維護(hù),。

  4. 對(duì)結(jié)果集解析存在硬編碼(查詢(xún)列名),,sql變化導(dǎo)致解析代碼變化,系統(tǒng)不易維護(hù),,如果能將數(shù)據(jù)庫(kù)記錄封裝成pojo(POJO是指簡(jiǎn)單的Java對(duì)象,,實(shí)際就是普通JavaBeans,是為了避免和EJB混淆所創(chuàng)造的簡(jiǎn)稱(chēng),。POJO通指沒(méi)有使用Entity Beans的普通java對(duì)象,,可以把POJO作為支持業(yè)務(wù)邏輯的協(xié)助類(lèi)。)對(duì)象解析比較方便。


6. MyBatis架構(gòu)

Configuration        MyBatis所有的配置信息都保存在Configuration對(duì)象之中,,配置文件中的大部分配置都會(huì)存儲(chǔ)到該類(lèi)中

SqlSession           作為MyBatis工作的主要頂層API,,表示和數(shù)據(jù)庫(kù)交互時(shí)的會(huì)話,完成必要數(shù)據(jù)庫(kù)增刪改查功能

Executor              MyBatis執(zhí)行器,,是MyBatis 調(diào)度的核心,,負(fù)責(zé)SQL語(yǔ)句的生成和查詢(xún)緩存的維護(hù)

StatementHandler  封裝了JDBC Statement操作,負(fù)責(zé)對(duì)JDBC statement 的操作,,如設(shè)置參數(shù)等

ParameterHandler   負(fù)責(zé)對(duì)用戶(hù)傳遞的參數(shù)轉(zhuǎn)換成JDBC Statement 所對(duì)應(yīng)的數(shù)據(jù)類(lèi)型

ResultSetHandler    負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對(duì)象轉(zhuǎn)換成List類(lèi)型的集合

TypeHandler          負(fù)責(zé)java數(shù)據(jù)類(lèi)型和jdbc數(shù)據(jù)類(lèi)型(也可以說(shuō)是數(shù)據(jù)表列類(lèi)型)之間的映射和轉(zhuǎn)換

MappedStatement   MappedStatement維護(hù)一條<select|update|delete|insert>節(jié)點(diǎn)的封裝

SqlSource              負(fù)責(zé)根據(jù)用戶(hù)傳遞的parameterObject,,動(dòng)態(tài)地生成SQL語(yǔ)句,將信息封裝到BoundSql對(duì)象中,,并返回

BoundSql              表示動(dòng)態(tài)生成的SQL語(yǔ)句以及相應(yīng)的參數(shù)信息


7. MyBatis配置(第一種總結(jié)方式

1).SqlMapConfig.xml,,此文件作為mybatis的全局(核心)配置文件,配置了mybatis的運(yùn)行環(huán)境等信息,。 

mapper.xml 文件即sql映射文件,,文件中配置了操作數(shù)據(jù)庫(kù)的sql語(yǔ)句。此文件需要在SqlMapConfig.xml中加載,。 例如:User.xml

2).通過(guò)mybatis環(huán)境等配置信息構(gòu)造SqlSessionFactory即會(huì)話工廠 例如:通過(guò)流的形式構(gòu)造SqlSessionFactory會(huì)話工廠

①I(mǎi)nputStream inputStream  = Resources.getResourceAsStream(sqlMapConfig.xml路徑位置);

②SqlSessionFactory sqlSessionFactory = new sqlSessionFactoryBuilder().build(inputStream);

3).由會(huì)話工廠創(chuàng)建sqlSession即會(huì)話,,操作數(shù)據(jù)庫(kù)需要通過(guò)sqlSession進(jìn)行。 sqlSession是線程不安全的,,每個(gè)線程都應(yīng)該有它獨(dú)自的sqlSession,,使用完就關(guān)閉。

4).mybatis底層自定義了Executor執(zhí)行器接口操作數(shù)據(jù)庫(kù),,Executor接口有兩個(gè)實(shí)現(xiàn),,一個(gè)是基本執(zhí)行器、一個(gè)是緩存執(zhí)行器,。 (Executor才是真正操作數(shù)據(jù)庫(kù)的,不過(guò)是底層 所以我們認(rèn)為是sqlSession在進(jìn)行操作)

5).Mapped Statement也是mybatis一個(gè)底層封裝對(duì)象,,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個(gè)sql對(duì)應(yīng)一個(gè)Mapped Statement對(duì)象,,sql的id即是Mapped statement的id,。 

6).Mapped Statement對(duì)sql執(zhí)行輸入?yún)?shù)進(jìn)行定義,包括HashMap,、基本類(lèi)型,、pojo,Executor通過(guò)Mapped Statement在執(zhí)行sql前將輸入的java對(duì)象映射至sql中,,輸入?yún)?shù)映射就是jdbc編程中對(duì)preparedStatement設(shè)置參數(shù),。 

7).Mapped Statement對(duì)sql執(zhí)行輸出結(jié)果進(jìn)行定義,包括HashMap,、基本類(lèi)型,、pojo,,Executor通過(guò)Mapped Statement在執(zhí)行sql后將輸出結(jié)果映射至java對(duì)象中,輸出結(jié)果映射過(guò)程相當(dāng)于jdbc編程中對(duì)結(jié)果的解析處理過(guò)程,。



8.Dao開(kāi)發(fā)(第一種總結(jié)方式)

8-1.傳統(tǒng)dao開(kāi)發(fā)

8-1.1.使用上面的UserMapper.xml和SqlMapConfig.xml

8-1.2.創(chuàng)建Dao接口 方法名要與UserMapper.xml中的sql的id一致

8-1.3.創(chuàng)建Dao實(shí)現(xiàn)類(lèi)

8-1.4.對(duì)Dao進(jìn)行junit測(cè)試

原始Dao開(kāi)發(fā)中存在以下問(wèn)題:

Dao方法體存在重復(fù)代碼:通過(guò)SqlSessionFactory創(chuàng)建SqlSession,,調(diào)用SqlSession的數(shù)據(jù)庫(kù)操作方法

調(diào)用sqlSession的數(shù)據(jù)庫(kù)操作方法需要指定statement的id,這里存在硬編碼,,不便于開(kāi)發(fā)維護(hù),。

8-2.Mapper動(dòng)態(tài)代理開(kāi)發(fā)

Mapper接口開(kāi)發(fā)方法只需要程序員編寫(xiě)Mapper接口(相當(dāng)于Dao接口),Mybatis框架根據(jù)接口定義創(chuàng)建接口的動(dòng)態(tài)代理對(duì)象,,代理對(duì)象的方法體同上邊Dao接口實(shí)現(xiàn)類(lèi)方法,。 

Mapper接口開(kāi)發(fā)需要遵循以下規(guī)范:

1).Mapper.xml文件中的namespace與mapper接口的類(lèi)路徑相同。

2).Mapper接口方法名和Mapper.xml中定義的每個(gè)statement的id相同

3).Mapper接口方法的輸入?yún)?shù)類(lèi)型和mapper.xml中定義的每個(gè)sql 的parameterType的類(lèi)型相同

4).Mapper接口方法的輸出參數(shù)類(lèi)型和mapper.xml中定義的每個(gè)sql的resultType的類(lèi)型相同

8-2.1.SqlMapConfig.xml

8-2.2.UserMapper.java 接口

8-2.3.UserMapper.xml 

8-2.4.使用junit進(jìn)行測(cè)試


9.MyBatis配置(第二種總結(jié)方式

全局配置文件:sqlmapconfig.xml

文件中的配置項(xiàng)是有順序的,按照官方圖來(lái)配. 
configuration 
- properties 
- settings 
- typeAliases 
- typeHandlers 
- objectFactory 
- plugins 
- environments 
* envioronment 
* transactionManager 
* dataSource 
- databaseIdProvider 
- mappers

例如:

settings: 全局參數(shù)設(shè)置 
設(shè)置延遲加載:

typeAliases:類(lèi)型別名

mappers:

映射文件

注意點(diǎn)

#{xxx}表示一個(gè)占位符,jdbc中的?通過(guò)#{xxx} 可以將外部傳遞過(guò)來(lái)映射到sql語(yǔ)句中,可以有效的防止sql注入. 
xxx表示一個(gè)sql串的拼接,可以有效防止sql注入.果是xxx個(gè)sql,sql.{xxx},輸入的參數(shù)類(lèi)型是簡(jiǎn)單類(lèi)型,那么${xxx}中的xxx必須是value.

parameterType:表示輸入?yún)?shù)的數(shù)據(jù)類(lèi)型 
resultType:輸出結(jié)果的數(shù)據(jù)類(lèi)型,如果查詢(xún)的是列表,那么resultType只需要設(shè)置列表中的元素的數(shù)據(jù)類(lèi)即可.

核心API

執(zhí)行流程: 
加載sqlmapconfig.xml,通過(guò)sqlsessionfactorybuilder,構(gòu)建sqlsesionfactroy對(duì)象.由它構(gòu)建sqlsession提供增刪改查等操作數(shù)據(jù)庫(kù)的方法.

SqlSessionFactoryBuilder

案例

SqlSessionFactory

創(chuàng)建SqlSession的方法:

用openSession方法獲取的SqlSession有以下特點(diǎn): 
- 開(kāi)啟事務(wù),但是不會(huì)自動(dòng)提交事務(wù) 
- 將從配置文件中配置的數(shù)據(jù)源中獲取連接(Connection) 
- 事務(wù)級(jí)別默認(rèn)使用驅(qū)動(dòng)或數(shù)據(jù)源的 
- 預(yù)編譯不會(huì)被重用,不會(huì)批量更新

列舉3個(gè)執(zhí)行類(lèi)型參數(shù)(ExecutorType): 
- ExecutorType.SIMPLE 每次執(zhí)行都預(yù)編譯 
- ExecutorType.REUSE 重用預(yù)編譯 
- ExecutorType.BATCH 批量預(yù)編譯

SqlSession

SqlSession執(zhí)行增刪改查以及事務(wù)操作.


10.Dao開(kāi)發(fā)(第二種總結(jié)方式)

實(shí)現(xiàn)方式: 
1. 傳統(tǒng)的Dao建一個(gè)dao 建立一個(gè)接口,再建實(shí)現(xiàn)類(lèi). 
2. mapper代理的方式,只需要寫(xiě)接口,不需要寫(xiě)實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)類(lèi)由mybatis框架自動(dòng)創(chuàng)建(官方推薦)

傳統(tǒng)的Dao

  • sqlsessionfactorybuilder當(dāng)作一個(gè)工具類(lèi),一旦使用完畢,就應(yīng)該銷(xiāo)毀,最佳使用范圍在方法內(nèi)部.

  • sqlsessionfactory要單例存在,一旦創(chuàng)建就應(yīng)當(dāng)在整個(gè)程序運(yùn)行期使用,沒(méi)必要?jiǎng)?chuàng)建一次.使用范圍整個(gè)運(yùn)行期.(整合spring時(shí),可以由spring來(lái)管理)

  • sqlsession是多例的,它線程不安全的,也不能被共享的,使用范圍是在方法的內(nèi)部.而且,一旦使用完成,必須要關(guān)閉,在finally中關(guān)閉.

接口:

實(shí)現(xiàn)類(lèi):

優(yōu)化: 
可以建立一個(gè)BaseDaoImpl 繼承sqlsesiondaosupport ,在此進(jìn)行sqlsessionfactory注入,。

mapper動(dòng)態(tài)代理的方式

mapper代理的方式,只需要寫(xiě)接口,不需要寫(xiě)實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)類(lèi)由mybatis框架自動(dòng)創(chuàng)建. 
需要遵守規(guī)則: 
1. sql的映射文件中的namespace要和Mapper接口中的類(lèi)路徑(全限定名)一致 
2. sql的映射文件中的sql的id要和mapper接口中的方法的名稱(chēng)一致 
3. sql的映射文件中的parameterType要和mapper接口中的方法的參數(shù)類(lèi)型一致 
4. sql的映射文件中的resultType要和mapper接口中的方法的返回值數(shù)據(jù)類(lèi)型一致


11. #{} 與 ${}區(qū)別

#{}表示一個(gè)占位符號(hào),,通過(guò)#{}可以實(shí)現(xiàn)preparedStatement向占位符中設(shè)置值,自動(dòng)進(jìn)行java類(lèi)型和jdbc類(lèi)型轉(zhuǎn)換,。#{}可以有效防止sql注入,。 #{}可以接收簡(jiǎn)單類(lèi)型值或pojo屬性值。 如果parameterType傳輸單個(gè)簡(jiǎn)單類(lèi)型值,,#{}括號(hào)中可以是value或其它名稱(chēng)。

${}表示拼接sql串,,通過(guò)${}可以將parameterType 傳入的內(nèi)容拼接在sql中且不進(jìn)行jdbc類(lèi)型轉(zhuǎn)換,, ${}可以接收簡(jiǎn)單類(lèi)型值或pojo屬性值,如果parameterType傳輸單個(gè)簡(jiǎn)單類(lèi)型值,,${}括號(hào)中只能是value,。

sql注入案例:"%"#{}"%" 如果#{}傳入的是<!-- 如果是字符串拼接${}會(huì)直接導(dǎo)致誤認(rèn)為注釋沒(méi)完成導(dǎo)致出錯(cuò)。


12.parameterType和resultType

parameterType:指定輸入?yún)?shù)類(lèi)型,,mybatis通過(guò)ognl從輸入對(duì)象中獲取參數(shù)值拼接在sql中,。

resultType:指定輸出結(jié)果類(lèi)型,mybatis將sql查詢(xún)結(jié)果的一行記錄數(shù)據(jù)映射為resultType指定類(lèi)型的對(duì)象,。如果有多條數(shù)據(jù),,則分別進(jìn)行映射,并把對(duì)象放到容器List中


13.selectOne和selectList

selectOne查詢(xún)一條記錄,,如果使用selectOne查詢(xún)多條記錄則拋出異常:

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3

at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)

selectList可以查詢(xún)一條或多條記錄,。


14.Mybatis解決jdbc編程的問(wèn)題

1).數(shù)據(jù)庫(kù)連接創(chuàng)建、釋放頻繁造成系統(tǒng)資源浪費(fèi)從而影響系統(tǒng)性能,,如果使用數(shù)據(jù)庫(kù)連接池可解決此問(wèn)題,。

解決:在SqlMapConfig.xml中配置數(shù)據(jù)連接池,使用連接池管理數(shù)據(jù)庫(kù)鏈接,。

2).Sql語(yǔ)句寫(xiě)在代碼中造成代碼不易維護(hù),,實(shí)際應(yīng)用sql變化的可能較大,,sql變動(dòng)需要改變java代碼。

解決:將Sql語(yǔ)句配置在XXXXmapper.xml文件中與java代碼分離,。

3).向sql語(yǔ)句傳參數(shù)麻煩,,因?yàn)閟ql語(yǔ)句的where條件不一定,可能多也可能少,,占位符需要和參數(shù)一一對(duì)應(yīng),。

解決:Mybatis自動(dòng)將java對(duì)象映射至sql語(yǔ)句,通過(guò)statement中的parameterType定義輸入?yún)?shù)的類(lèi)型,。

4).對(duì)結(jié)果集解析麻煩,,sql變化導(dǎo)致解析代碼變化,且解析前需要遍歷,,如果能將數(shù)據(jù)庫(kù)記錄封裝成pojo對(duì)象解析比較方便,。

解決:Mybatis自動(dòng)將sql執(zhí)行結(jié)果映射至java對(duì)象,通過(guò)statement中的resultType定義輸出結(jié)果的類(lèi)型,。


15.分頁(yè)查詢(xún)

ResultHandler參數(shù):可以把每行記錄包裝成其他數(shù)據(jù)類(lèi)型,如:List,Map,Set.只需要寫(xiě)個(gè)接口.


16.控制事務(wù)


17.緩存

一級(jí)緩存指的就是sqlsession,,在sqlsession中有一個(gè)數(shù)據(jù)區(qū)域,是map結(jié)構(gòu),,這個(gè)區(qū)域就是一級(jí)緩存區(qū)域,。一級(jí)緩存中的key是由sql語(yǔ)句、條件,、statement等信息組成一個(gè)唯一值,。一級(jí)緩存中的value,就是查詢(xún)出的結(jié)果對(duì)象,。(本地緩存)

二級(jí)緩存指的就是同一個(gè)namespace下的mapper,,二級(jí)緩存中,也有一個(gè)map結(jié)構(gòu),,這個(gè)區(qū)域就是一級(jí)緩存區(qū)域,。一級(jí)緩存中的key是由sql語(yǔ)句、條件,、statement等信息組成一個(gè)唯一值,。一級(jí)緩存中的value,就是查詢(xún)出的結(jié)果對(duì)象,。(自定義緩存)

一級(jí)緩存是默認(rèn)使用的,。 
二級(jí)緩存需要手動(dòng)開(kāi)啟。

二級(jí)緩存的配置 
1. 開(kāi)啟二級(jí)緩存中開(kāi)關(guān)

2. 在mapper映射文件中開(kāi)啟二級(jí)緩存

3. 禁止使用緩存 
useCache=”false” 
4. 刷新緩存 
select語(yǔ)句默認(rèn)是false,增刪改語(yǔ)句默認(rèn)是true


18.Mappers

Mapper是個(gè)接口,里面定義方法匹配SqlSession中方法.

Mapper注解,參考Mybatis官方文檔. 
示例:


19.動(dòng)態(tài)sql

MyBatis使用OGNL表達(dá)式,來(lái)支持一些常用的標(biāo)簽. 
- if 
- choose(when,otherwise) 
- trim(where,set) 
- foreach

案例:

if

choose, when, otherwise

trim, where, set

foreach

bind

創(chuàng)建一個(gè)ognl表達(dá)式,用于語(yǔ)句中.

Multi-db vendor support

Pluggable Scripting Languages For Dynamic SQL

為動(dòng)態(tài)sql定制可插拔腳本語(yǔ)句

使用步驟: 
實(shí)現(xiàn)LanguageDriver接口

再mybatis-config.xml或sqlmapconfig.xml文件中覆蓋默認(rèn)別名,使用自定義

在映射文件中使用

也可以在mapper中使用

sql片段

抽取sql重復(fù)代碼,,提供效率,。


20.多表查詢(xún)

輸出結(jié)果類(lèi)型

  1. resultType,字段和java對(duì)象中屬性一致推薦使用resultType

  2. resultMap,字段和java對(duì)象中屬性不一致的情況比較多的時(shí)候使用resultMap

ResultMap

resultType使用時(shí),設(shè)置值時(shí)需要查詢(xún)的列名和java對(duì)象屬性名一致.如果不一致的時(shí)候,可以使用resultMap.使用時(shí)需要先定義,再使用. 
resultMap做復(fù)雜的映射配置(多表查詢(xún)).

映射一對(duì)一關(guān)系

映射一對(duì)多關(guān)系

多對(duì)多映射


21.mybatis和spring的整合

整合的步驟 
1). 創(chuàng)建工程 
2). 加入jar spring的包 mybatis的包 依賴(lài)包 數(shù)據(jù)庫(kù)驅(qū)動(dòng) 數(shù)據(jù)連接池 整合包 日志包 
3). mybatis配置文件

4). spring的配置文件

5). 數(shù)據(jù)庫(kù)、日志的配置文件 
db.properties

log4j.properties


MyBatis使用中幾點(diǎn)經(jīng)驗(yàn)

1.手動(dòng)增量配置映射文件

當(dāng)有工具生成Mapper等配置文件的時(shí)候,,很多人就不愿意手動(dòng)寫(xiě)了,。其實(shí)MyBatis的生成工具不是特別有用,,生成的方法幾乎不可用,刪刪改改老半天還不如自己手寫(xiě)快,。而且需要新加或修改屬性,、方法時(shí),也是沒(méi)法使用生成的文件,,因?yàn)樾枰A艉迷械囊恍傩院头椒?。手?xiě)映射文件時(shí)先定義出用到的字段,這樣配置文件會(huì)簡(jiǎn)潔清晰,,同時(shí)結(jié)果映射時(shí)效率會(huì)更高,。

2.Mapper層參數(shù)為Map,由Service層負(fù)責(zé)重載。
    Mapper由于機(jī)制的問(wèn)題,,不能重載,,參數(shù)一般設(shè)置成Map,但這樣會(huì)使參數(shù)變得模糊,,如果想要使代碼變得清晰,,可以通過(guò)service層來(lái)實(shí) 現(xiàn)重載的目的,對(duì)外提供的Service層是重載的,,但這些重載的Service方法其實(shí)是調(diào)同一個(gè)Mapper,,只不過(guò)相應(yīng)的參數(shù)并不一致。
    也許有人會(huì)想,,為什么不在Service層也設(shè)置成Map呢,?我個(gè)人是不推薦這么做的,雖然為了方便,,我在之前的項(xiàng)目中也大量采用了這種方式,,但 很明顯會(huì)給日后的維護(hù)工作帶來(lái)麻煩,。因?yàn)檫@么做會(huì)使你整個(gè)MVC都依賴(lài)于Map模型,,這個(gè)模型其實(shí)是很不錯(cuò)的,方便搭框架,,但存在一個(gè)問(wèn)題:僅僅看方法簽 名,你不清楚Map中所擁有的參數(shù)個(gè)數(shù)、類(lèi)型,、每個(gè)參數(shù)代表的含義,。 
    試想,你只對(duì)Service層變更,,或者DAO層變更,,你需要清楚整個(gè)流程中Map傳遞過(guò)來(lái)的參數(shù),除非你注釋或者文檔良好,,否則必須把每一層的 代碼都了解清楚,,你才知道傳遞了哪些參數(shù),。針對(duì)于簡(jiǎn)單MVC,那倒也還好,,但如果層次復(fù)雜之后,,代碼會(huì)變得異常復(fù)雜,而且如果我增加一個(gè)參數(shù),,需要把每一 個(gè)層的注釋都添加上,。相對(duì)于注釋?zhuān)褂梅椒ê灻麃?lái)保證這種代碼可控性會(huì)來(lái)得更可行一些,因?yàn)樽⑨層锌赡苁沁^(guò)時(shí)的,,但方法簽名一般不太可能是陳舊的,。

3.盡量少用if choose等語(yǔ)句,降低維護(hù)的難度,。
    Mybatis的配置SQL時(shí),,盡量少用if choose 等標(biāo)簽,能用SQL實(shí)現(xiàn)判斷的盡量用SQL來(lái)判斷(CASE WHEN ,DECODE等),,以便后期維護(hù),。否則,一旦SQL膨脹,,超級(jí)惡心,,如果需要調(diào)試Mybatis中的SQL,需要去除大量的判斷語(yǔ)句,,非常麻煩,。另一方 面,大量的if判斷,,會(huì)使生成的SQL中包含大量的空格,,增加網(wǎng)絡(luò)傳輸?shù)臅r(shí)間,也不可取,。

    而且大量的if choose語(yǔ)句,,不可避免地,每次生成的SQL會(huì)不太一致,,會(huì)導(dǎo)致ORACLE大量的硬解析,,也不可取。 
我們來(lái)看看這樣的SQL

Xml代碼  

     這樣的if判斷,,其實(shí)是完全沒(méi)有必要的,我們可以很簡(jiǎn)單的采用DECODE來(lái)解決默認(rèn)值問(wèn)題:

Xml代碼  

    當(dāng)然有人會(huì)想,,引入CASE WHEN,DECODE會(huì)導(dǎo)致需要ORACLE函數(shù)解析,會(huì)拖慢SQL執(zhí)行時(shí)間,,有興趣的同學(xué)可以回去做一下測(cè)試,,看看是否會(huì)有大的影響。就個(gè)人經(jīng)驗(yàn)而 言,,在我的開(kāi)發(fā)過(guò)程,,沒(méi)有發(fā)現(xiàn)因?yàn)楹瘮?shù)解析導(dǎo)致SQL變慢的情形,。影響SQL執(zhí)行效率的一般情況下是JOIN、ORDER BY,、DISTINCT,、PARTITATION BY等這些操作,這些操作一般與表結(jié)構(gòu)設(shè)計(jì)有很大的關(guān)聯(lián),。相對(duì)于這些的效率影響程度,,函數(shù)解析對(duì)于SQL執(zhí)行速度影響應(yīng)該是可以忽略不計(jì)的。

    另外一點(diǎn),,對(duì)于一些默認(rèn)值的賦值,,像上面那條SQL,默認(rèn)成當(dāng)前日期什么的,,其實(shí)可以完全提到Service層或Controller層做處理,,在Mybatis中應(yīng)該要少用這些判斷。因?yàn)?,這樣的話,,很難做緩存處理。如果startdate為空,,在SQL上使用動(dòng)態(tài)的SYSDATE,,就無(wú)法確定緩存startdate日期的key應(yīng)該是什么了。所以參數(shù)最好在傳遞至Mybatis之前都處理好,,這樣Mybatis層也能減少部分if choose語(yǔ)句,,同時(shí)也方便做緩存處理。

    當(dāng)然不使用if choose也并不是絕對(duì)的,,有時(shí)候?yàn)榱藘?yōu)化SQL,,不得不使用if來(lái)解決,比如說(shuō)LIKE語(yǔ)句,,當(dāng)然一般不推薦使用LIKE,,但如果存在使用的場(chǎng)景,盡 可能在不需要使用時(shí)候去除LIKE,,比如查詢(xún)文章標(biāo)題,,以提高查詢(xún)效率,。 最好的方式是使用lucence等搜索引擎來(lái)解決這種全文索引的問(wèn)題,。

    總的來(lái)說(shuō),if與choose判斷分支是不可能完全去除的,,但是推薦使用SQL原生的方式來(lái)解決一些動(dòng)態(tài)問(wèn)題,,而不應(yīng)該完全依賴(lài)Mybatis來(lái)完成動(dòng)態(tài)分支的判斷,因?yàn)榕袛喾种н^(guò)于復(fù)雜,,而且難以維護(hù),。 

4.用XML注釋取代SQL注釋,。
    Mybatis中原SQL的注釋盡量不要保留,注釋會(huì)引發(fā)一些問(wèn)題,,如果需要使用注釋?zhuān)梢栽赬ML中用來(lái)注釋?zhuān)WC在生成的SQL中不會(huì)存在SQL注釋?zhuān)瑥亩档蛦?wèn)題出現(xiàn)的可能性,。這樣做還有一個(gè)好處,就是在IDE中可以很清楚的區(qū)分注釋與 SQL,。

    現(xiàn)在來(lái)談?wù)勛⑨屢l(fā)的問(wèn)題,,我做的一個(gè)項(xiàng)目中,分頁(yè)組件是基于Mybatis的,,它會(huì)在你寫(xiě)的SQL腳本外面再套一層SELECT COUNT(*) ROWNUM_ FROM (....) 計(jì)算總記錄數(shù),,同時(shí)有另一個(gè)嵌套

Xml代碼

    即使傳入的參數(shù)中存在對(duì)應(yīng)的參數(shù),實(shí)際也不會(huì)產(chǎn)生效果,,因?yàn)楹竺娴膬?nèi)容實(shí)際上是被完全注釋了,。這種錯(cuò)誤,,如果不經(jīng)過(guò)嚴(yán)格的測(cè)試,,是很難發(fā)現(xiàn)的。一般情況下,,XML注釋完全可以替代SQL注釋?zhuān)虼诉@種行為應(yīng)該可以禁止掉,。

5.盡可能使用#{},而不是${}.
    Mybatis中盡量不要使用${},,盡量這樣做很方便開(kāi)發(fā),,但是有一個(gè)問(wèn)題,就是大量使用會(huì)導(dǎo)致ORACLE的硬解析,,拖慢數(shù)據(jù)庫(kù)性能,,運(yùn)行越久,數(shù)據(jù)庫(kù)性能會(huì)越差,。對(duì)于一般多個(gè)字符串IN的處理,,可以參考文章最后的解決方案基本可以解決大部分${}.

    關(guān)于${},另一個(gè)誤用的地方就是LIKE,,我這邊還有個(gè)案例:比如一些樹(shù)型菜單,,節(jié)點(diǎn)會(huì)設(shè)計(jì)成'01','0101',用兩位節(jié)點(diǎn)來(lái)區(qū)分層級(jí),這時(shí)候,, 如果需要查詢(xún)01節(jié)點(diǎn)下所有的節(jié)點(diǎn),,最簡(jiǎn)單的SQL便是:SELECT * FROM TREE WHERE ID LIKE '01%',這種SQL其實(shí)無(wú)可厚非,因?yàn)樗材苡玫剿饕?,所以不需要特別的處理,,直接使用就行了。但如果是文章標(biāo)題,則需要額外注意了:SELECT * FROM T_NEWS_TEXT WHERE TITLE LIKE '%OSC%',,這是怎么也不會(huì)用到索引的,,上面說(shuō)了,最好采用全文檢索,。但如果離不開(kāi)LIKE,,就需要注意使用的方式: ID LIKE #{ID} || '%'而不是ID LIKE '${ID}%',減少硬解析的可能,。

    有人覺(jué)得使用||會(huì)增加ORACLE處理的時(shí)間,,我覺(jué)得不要把ORACLE看得太傻,雖然有時(shí)候確實(shí)非常傻,,有空可以再總結(jié)ORACLE傻不垃圾的地方,,但是稍加測(cè)試便知:這種串聯(lián)方式,對(duì)于整個(gè)SQL的解析執(zhí)行,,應(yīng)該是微乎其微的,。

    當(dāng)然還有一些特殊情況是沒(méi)有辦法處理的,比如說(shuō)動(dòng)態(tài)注入列名,、表名等,。對(duì)于這些情況,則比較棘手,,沒(méi)有找到比較方便的手段,。由于這種情況出現(xiàn)的可 能性會(huì)比較少,所以使用${}倒也不至于有什么太大的影響,。當(dāng)然你如果有代碼潔癖的話,,可以使用ORACLE的動(dòng)態(tài)執(zhí)行SQL的機(jī)制Execute immediate,這樣就可以完全避免${}出現(xiàn)的可能性了,。這樣會(huì)引入比較復(fù)雜的模型,,這個(gè)時(shí)候,你就需要取舍了,。

    針對(duì)于以上動(dòng)態(tài)SQL所導(dǎo)致的問(wèn)題,,最激進(jìn)的方式是全部采用存儲(chǔ)過(guò)程,用數(shù)據(jù)庫(kù)原生的方式來(lái)解決,,方便開(kāi)發(fā)調(diào)試,,當(dāng)然也會(huì)帶來(lái)問(wèn)題:對(duì)開(kāi)發(fā)人員會(huì)有更高的要求、存儲(chǔ)過(guò)程的管理等等,,我這邊項(xiàng)目沒(méi)有采用過(guò)這種方式,,這里不做更多的展開(kāi)。 


Mybatis中設(shè)計(jì)模式:

Mybatis至少遇到了以下的設(shè)計(jì)模式的使用:

  1. Builder模式,,例如SqlSessionFactoryBuilder,、XMLConfigBuilderXMLMapperBuilder,、XMLStatementBuilder,、CacheBuilder

  2. 工廠模式,,例如SqlSessionFactory,、ObjectFactoryMapperProxyFactory,;

  3. 單例模式,,例如ErrorContextLogFactory

  4. 代理模式,,Mybatis實(shí)現(xiàn)的核心,,比如MapperProxyConnectionLogger,,用的jdk的動(dòng)態(tài)代理,;還有executor.loader包使用了cglib或者javassist達(dá)到延遲加載的效果;

  5. 組合模式,,例如SqlNode和各個(gè)子類(lèi)ChooseSqlNode等,;

  6. 模板方法模式,例如BaseExecutorSimpleExecutor,,還有BaseTypeHandler和所有的子類(lèi)例如IntegerTypeHandler,;

  7. 適配器模式,例如LogMybatis接口和它對(duì)jdbc,、log4j等各種日志框架的適配實(shí)現(xiàn),;

  8. 裝飾者模式,例如Cache包中的cache.decorators子包中等各個(gè)裝飾者的實(shí)現(xiàn),;

  9. 迭代器模式,,例如迭代器模式PropertyTokenizer

接下來(lái)挨個(gè)模式進(jìn)行解讀,,先介紹模式自身的知識(shí),,然后解讀在Mybatis中怎樣應(yīng)用了該模式。

1,、Builder模式

Builder模式的定義是“將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示?!?,它屬于創(chuàng)建類(lèi)模式,一般來(lái)說(shuō),,如果一個(gè)對(duì)象的構(gòu)建比較復(fù)雜,,超出了構(gòu)造函數(shù)所能包含的范圍,,就可以使用工廠模式和Builder模式,相對(duì)于工廠模式會(huì)產(chǎn)出一個(gè)完整的產(chǎn)品,,Builder應(yīng)用于更加復(fù)雜的對(duì)象的構(gòu)建,,甚至只會(huì)構(gòu)建產(chǎn)品的一個(gè)部分。

在Mybatis環(huán)境的初始化過(guò)程中,,SqlSessionFactoryBuilder會(huì)調(diào)用XMLConfigBuilder讀取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,,構(gòu)建Mybatis運(yùn)行的核心對(duì)象Configuration對(duì)象,然后將該Configuration對(duì)象作為參數(shù)構(gòu)建一個(gè)SqlSessionFactory對(duì)象,。

其中XMLConfigBuilder在構(gòu)建Configuration對(duì)象時(shí),,也會(huì)調(diào)用XMLMapperBuilder用于讀取*Mapper文件,而XMLMapperBuilder會(huì)使用XMLStatementBuilder來(lái)讀取和build所有的SQL語(yǔ)句,。

在這個(gè)過(guò)程中,,有一個(gè)相似的特點(diǎn),就是這些Builder會(huì)讀取文件或者配置,,然后做大量的XpathParser解析,、配置或語(yǔ)法的解析、反射生成對(duì)象,、存入結(jié)果緩存等步驟,,這么多的工作都不是一個(gè)構(gòu)造函數(shù)所能包括的,因此大量采用了Builder模式來(lái)解決,。

對(duì)于builder的具體類(lèi),,方法都大都用build*開(kāi)頭,比如SqlSessionFactoryBuilder為例,,它包含以下方法:

即根據(jù)不同的輸入?yún)?shù)來(lái)構(gòu)建SqlSessionFactory這個(gè)工廠對(duì)象,。

2、工廠模式

在Mybatis中比如SqlSessionFactory使用的是工廠模式,,該工廠沒(méi)有那么復(fù)雜的邏輯,,是一個(gè)簡(jiǎn)單工廠模式。

簡(jiǎn)單工廠模式(Simple Factory Pattern):又稱(chēng)為靜態(tài)工廠方法(Static Factory Method)模式,,它屬于類(lèi)創(chuàng)建型模式,。在簡(jiǎn)單工廠模式中,可以根據(jù)參數(shù)的不同返回不同類(lèi)的實(shí)例,。簡(jiǎn)單工廠模式專(zhuān)門(mén)定義一個(gè)類(lèi)來(lái)負(fù)責(zé)創(chuàng)建其他類(lèi)的實(shí)例,,被創(chuàng)建的實(shí)例通常都具有共同的父類(lèi)。

SqlSession可以認(rèn)為是一個(gè)Mybatis工作的核心的接口,,通過(guò)這個(gè)接口可以執(zhí)行執(zhí)行SQL語(yǔ)句,、獲取Mappers、管理事務(wù),。類(lèi)似于連接MySQL的Connection對(duì)象,。

可以看到,,該Factory的openSession方法重載了很多個(gè),分別支持autoCommit,、Executor,、Transaction等參數(shù)的輸入,來(lái)構(gòu)建核心的SqlSession對(duì)象,。

在DefaultSqlSessionFactory的默認(rèn)工廠實(shí)現(xiàn)里,,有一個(gè)方法可以看出工廠怎么產(chǎn)出一個(gè)產(chǎn)品:

這是一個(gè)openSession調(diào)用的底層方法,,該方法先從configuration讀取對(duì)應(yīng)的環(huán)境配置,,然后初始化TransactionFactory獲得一個(gè)Transaction對(duì)象,然后通過(guò)Transaction獲取一個(gè)Executor對(duì)象,,最后通過(guò)configuration,、Executor、是否autoCommit三個(gè)參數(shù)構(gòu)建了SqlSession,。

在這里其實(shí)也可以看到端倪,,SqlSession的執(zhí)行,其實(shí)是委托給對(duì)應(yīng)的Executor來(lái)進(jìn)行的,。

而對(duì)于LogFactory,,它的實(shí)現(xiàn)代碼:

這里有個(gè)特別的地方,是Log變量的的類(lèi)型是Constructor<? extends Log>,,也就是說(shuō)該工廠生產(chǎn)的不只是一個(gè)產(chǎn)品,,而是具有Log公共接口的一系列產(chǎn)品,比如Log4jImpl,、Slf4jImpl等很多具體的Log,。

3、單例模式

單例模式(Singleton Pattern):?jiǎn)卫J酱_保某一個(gè)類(lèi)只有一個(gè)實(shí)例,,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例,,這個(gè)類(lèi)稱(chēng)為單例類(lèi),它提供全局訪問(wèn)的方法,。

單例模式的要點(diǎn)有三個(gè):一是某個(gè)類(lèi)只能有一個(gè)實(shí)例,;二是它必須自行創(chuàng)建這個(gè)實(shí)例;三是它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例,。單例模式是一種對(duì)象創(chuàng)建型模式,。單例模式又名單件模式或單態(tài)模式。

在Mybatis中有兩個(gè)地方用到單例模式,,ErrorContext和LogFactory,,其中ErrorContext是用在每個(gè)線程范圍內(nèi)的單例,用于記錄該線程的執(zhí)行環(huán)境錯(cuò)誤信息,,而LogFactory則是提供給整個(gè)Mybatis使用的日志工廠,,用于獲得針對(duì)項(xiàng)目配置好的日志對(duì)象,。

ErrorContext的單例實(shí)現(xiàn)代碼:

構(gòu)造函數(shù)是private修飾,具有一個(gè)static的局部instance變量和一個(gè)獲取instance變量的方法,,在獲取實(shí)例的方法中,,先判斷是否為空如果是的話就先創(chuàng)建,然后返回構(gòu)造好的對(duì)象,。

只是這里有個(gè)有趣的地方是,,LOCAL的靜態(tài)實(shí)例變量使用了ThreadLocal修飾,也就是說(shuō)它屬于每個(gè)線程各自的數(shù)據(jù),,而在instance()方法中,,先獲取本線程的該實(shí)例,如果沒(méi)有就創(chuàng)建該線程獨(dú)有的ErrorContext,。

4,、代理模式

代理模式可以認(rèn)為是Mybatis的核心使用的模式,正是由于這個(gè)模式,,我們只需要編寫(xiě)Mapper.java接口,,不需要實(shí)現(xiàn),由Mybatis后臺(tái)幫我們完成具體SQL的執(zhí)行,。

代理模式(Proxy Pattern) :給某一個(gè)對(duì)象提供一個(gè)代 理,,并由代理對(duì)象控制對(duì)原對(duì)象的引用。代理模式的英 文叫做Proxy或Surrogate,,它是一種對(duì)象結(jié)構(gòu)型模式,。

代理模式包含如下角色:

  • Subject: 抽象主題角色

  • Proxy: 代理主題角色

  • RealSubject: 真實(shí)主題角色

這里有兩個(gè)步驟,第一個(gè)是提前創(chuàng)建一個(gè)Proxy,,第二個(gè)是使用的時(shí)候會(huì)自動(dòng)請(qǐng)求Proxy,,然后由Proxy來(lái)執(zhí)行具體事務(wù);

當(dāng)我們使用Configuration的getMapper方法時(shí),,會(huì)調(diào)用mapperRegistry.getMapper方法,,而該方法又會(huì)調(diào)用mapperProxyFactory.newInstance(sqlSession)來(lái)生成一個(gè)具體的代理:

在這里,先通過(guò)T newInstance(SqlSession sqlSession)方法會(huì)得到一個(gè)MapperProxy對(duì)象,,然后調(diào)用T newInstance(MapperProxy<T> mapperProxy)生成代理對(duì)象然后返回,。

而查看MapperProxy的代碼,可以看到如下內(nèi)容:

非常典型的,,該MapperProxy類(lèi)實(shí)現(xiàn)了InvocationHandler接口,,并且實(shí)現(xiàn)了該接口的invoke方法。

通過(guò)這種方式,,我們只需要編寫(xiě)Mapper.java接口類(lèi),,當(dāng)真正執(zhí)行一個(gè)Mapper接口的時(shí)候,就會(huì)轉(zhuǎn)發(fā)給MapperProxy.invoke方法,,而該方法則會(huì)調(diào)用后續(xù)的sqlSession.cud>executor.execute>prepareStatement等一系列方法,,完成SQL的執(zhí)行和返回,。

5、組合模式

組合模式組合多個(gè)對(duì)象形成樹(shù)形結(jié)構(gòu)以表示“整體-部分”的結(jié)構(gòu)層次,。

組合模式對(duì)單個(gè)對(duì)象(葉子對(duì)象)和組合對(duì)象(組合對(duì)象)具有一致性,,它將對(duì)象組織到樹(shù)結(jié)構(gòu)中,可以用來(lái)描述整體與部分的關(guān)系,。同時(shí)它也模糊了簡(jiǎn)單元素(葉子對(duì)象)和復(fù)雜元素(容器對(duì)象)的概念,,使得客戶(hù)能夠像處理簡(jiǎn)單元素一樣來(lái)處理復(fù)雜元素,從而使客戶(hù)程序能夠與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦,。

在使用組合模式中需要注意一點(diǎn)也是組合模式最關(guān)鍵的地方:葉子對(duì)象和組合對(duì)象實(shí)現(xiàn)相同的接口,。這就是組合模式能夠?qū)⑷~子節(jié)點(diǎn)和對(duì)象節(jié)點(diǎn)進(jìn)行一致處理的原因。

Mybatis支持動(dòng)態(tài)SQL的強(qiáng)大功能,,比如下面的這個(gè)SQL:


在這里面使用到了trim,、if等動(dòng)態(tài)元素,,可以根據(jù)條件來(lái)生成不同情況下的SQL,;

在DynamicSqlSource.getBoundSql方法里,調(diào)用了rootSqlNode.apply(context)方法,,apply方法是所有的動(dòng)態(tài)節(jié)點(diǎn)都實(shí)現(xiàn)的接口:

對(duì)于實(shí)現(xiàn)該SqlSource接口的所有節(jié)點(diǎn),,就是整個(gè)組合模式樹(shù)的各個(gè)節(jié)點(diǎn):

組合模式的簡(jiǎn)單之處在于,所有的子節(jié)點(diǎn)都是同一類(lèi)節(jié)點(diǎn),,可以遞歸的向下執(zhí)行,,比如對(duì)于TextSqlNode,因?yàn)樗亲畹讓拥娜~子節(jié)點(diǎn),,所以直接將對(duì)應(yīng)的內(nèi)容append到SQL語(yǔ)句中:

但是對(duì)于IfSqlNode,,就需要先做判斷,如果判斷通過(guò),,仍然會(huì)調(diào)用子元素的SqlNode,,即contents.apply方法,實(shí)現(xiàn)遞歸的解析,。


6,、模板方法模式

模板方法模式是所有模式中最為常見(jiàn)的幾個(gè)模式之一,是基于繼承的代碼復(fù)用的基本技術(shù),。

模板方法模式需要開(kāi)發(fā)抽象類(lèi)和具體子類(lèi)的設(shè)計(jì)師之間的協(xié)作,。一個(gè)設(shè)計(jì)師負(fù)責(zé)給出一個(gè)算法的輪廓和骨架,另一些設(shè)計(jì)師則負(fù)責(zé)給出這個(gè)算法的各個(gè)邏輯步驟,。代表這些具體邏輯步驟的方法稱(chēng)做基本方法(primitive method),;而將這些基本方法匯總起來(lái)的方法叫做模板方法(template method),這個(gè)設(shè)計(jì)模式的名字就是從此而來(lái),。

模板類(lèi)定義一個(gè)操作中的算法的骨架,,而將一些步驟延遲到子類(lèi)中,。使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。

在Mybatis中,,sqlSession的SQL執(zhí)行,,都是委托給Executor實(shí)現(xiàn)的,Executor包含以下結(jié)構(gòu):

其中的BaseExecutor就采用了模板方法模式,,它實(shí)現(xiàn)了大部分的SQL執(zhí)行邏輯,,然后把以下幾個(gè)方法交給子類(lèi)定制化完成:

該模板方法類(lèi)有幾個(gè)子類(lèi)的具體實(shí)現(xiàn),使用了不同的策略:

  • 簡(jiǎn)單SimpleExecutor每執(zhí)行一次updateselect,,就開(kāi)啟一個(gè)Statement對(duì)象,,用完立刻關(guān)閉Statement對(duì)象。(可以是StatementPrepareStatement對(duì)象)

  • 重用ReuseExecutor執(zhí)行updateselect,,以sql作為key查找Statement對(duì)象,,存在就使用,不存在就創(chuàng)建,,用完后,,不關(guān)閉Statement對(duì)象,而是放置于Map<String, Statement>內(nèi),,供下一次使用,。(可以是StatementPrepareStatement對(duì)象)

  • 批量BatchExecutor執(zhí)行update(沒(méi)有selectJDBC批處理不支持select),,將所有sql都添加到批處理中(addBatch()),,等待統(tǒng)一執(zhí)行(executeBatch()),它緩存了多個(gè)Statement對(duì)象,,每個(gè)Statement對(duì)象都是addBatch()完畢后,,等待逐一執(zhí)行executeBatch()批處理的;BatchExecutor相當(dāng)于維護(hù)了多個(gè)桶,,每個(gè)桶里都裝了很多屬于自己的SQL,,就像蘋(píng)果藍(lán)里裝了很多蘋(píng)果,番茄藍(lán)里裝了很多番茄,,最后,,再統(tǒng)一倒進(jìn)倉(cāng)庫(kù)。(可以是StatementPrepareStatement對(duì)象)

比如在SimpleExecutor中這樣實(shí)現(xiàn)update方法:

7,、適配器模式

適配器模式(Adapter Pattern) :將一個(gè)接口轉(zhuǎn)換成客戶(hù)希望的另一個(gè)接口,,適配器模式使接口不兼容的那些類(lèi)可以一起工作,其別名為包裝器(Wrapper),。適配器模式既可以作為類(lèi)結(jié)構(gòu)型模式,,也可以作為對(duì)象結(jié)構(gòu)型模式。

在Mybatsi的logging包中,有一個(gè)Log接口:

該接口定義了Mybatis直接使用的日志方法,,而Log接口具體由誰(shuí)來(lái)實(shí)現(xiàn)呢,?Mybatis提供了多種日志框架的實(shí)現(xiàn),這些實(shí)現(xiàn)都匹配這個(gè)Log接口所定義的接口方法,,最終實(shí)現(xiàn)了所有外部日志框架到Mybatis日志包的適配:

比如對(duì)于Log4jImpl的實(shí)現(xiàn)來(lái)說(shuō),,該實(shí)現(xiàn)持有了org.apache.log4j.Logger的實(shí)例,然后所有的日志方法,,均委托該實(shí)例來(lái)實(shí)現(xiàn),。

8、裝飾者模式

裝飾模式(Decorator Pattern) :動(dòng)態(tài)地給一個(gè)對(duì)象增加一些額外的職責(zé)(Responsibility),,就增加對(duì)象功能來(lái)說(shuō),,裝飾模式比生成子類(lèi)實(shí)現(xiàn)更為靈活。其別名也可以稱(chēng)為包裝器(Wrapper),,與適配器模式的別名相同,,但它們適用于不同的場(chǎng)合。根據(jù)翻譯的不同,,裝飾模式也有人稱(chēng)之為“油漆工模式”,,它是一種對(duì)象結(jié)構(gòu)型模式。

在mybatis中,,緩存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定義,。整個(gè)體系采用裝飾器設(shè)計(jì)模式,,數(shù)據(jù)存儲(chǔ)和緩存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久緩存實(shí)現(xiàn),,然后通過(guò)一系列的裝飾器來(lái)對(duì)PerpetualCache永久緩存進(jìn)行緩存策略等方便的控制。如下圖:

用于裝飾PerpetualCache的標(biāo)準(zhǔn)裝飾器共有8個(gè)(全部在org.apache.ibatis.cache.decorators包中):

  • FifoCache先進(jìn)先出算法,,緩存回收策略

  • LoggingCache輸出緩存命中的日志信息

  • LruCache最近最少使用算法,,緩存回收策略

  • ScheduledCache調(diào)度緩存,負(fù)責(zé)定時(shí)清空緩存

  • SerializedCache緩存序列化和反序列化存儲(chǔ)

  • SoftCache基于軟引用實(shí)現(xiàn)的緩存管理策略

  • SynchronizedCache同步的緩存裝飾器,,用于防止多線程并發(fā)訪問(wèn)

  • WeakCache基于弱引用實(shí)現(xiàn)的緩存管理策略

另外,,還有一個(gè)特殊的裝飾器TransactionalCache:事務(wù)性的緩存

正如大多數(shù)持久層框架一樣,mybatis緩存同樣分為一級(jí)緩存和二級(jí)緩存

  • 一級(jí)緩存,,又叫本地緩存,,PerpetualCache類(lèi)型的永久緩存,保存在執(zhí)行器中(BaseExecutor),,而執(zhí)行器又在SqlSessionDefaultSqlSession)中,,所以一級(jí)緩存的生命周期與SqlSession是相同的。

  • 二級(jí)緩存,,又叫自定義緩存,,實(shí)現(xiàn)了Cache接口的類(lèi)都可以作為二級(jí)緩存,所以可配置如encache等的第三方緩存,。二級(jí)緩存以namespace名稱(chēng)空間為其唯一標(biāo)識(shí),,被保存在Configuration核心配置對(duì)象中,。二級(jí)緩存對(duì)象的默認(rèn)類(lèi)型為PerpetualCache,如果配置的緩存是默認(rèn)類(lèi)型,,則mybatis會(huì)根據(jù)配置自動(dòng)追加一系列裝飾器,。

Cache對(duì)象之間的引用順序?yàn)椋?/strong>SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache

9、迭代器模式

迭代器(Iterator)模式,,又叫做游標(biāo)(Cursor)模式,。GOF給出的定義為:提供一種方法訪問(wèn)一個(gè)容器(container)對(duì)象中各個(gè)元素,而又不需暴露該對(duì)象的內(nèi)部細(xì)節(jié),。

Java的Iterator就是迭代器模式的接口,,只要實(shí)現(xiàn)了該接口,就相當(dāng)于應(yīng)用了迭代器模式:

比如Mybatis的PropertyTokenizer是property包中的重量級(jí)類(lèi),,該類(lèi)會(huì)被reflection包中其他的類(lèi)頻繁的引用到,。這個(gè)類(lèi)實(shí)現(xiàn)了Iterator接口,在使用時(shí)經(jīng)常被用到的是Iterator接口中的hasNext這個(gè)函數(shù),。

可以看到,,這個(gè)類(lèi)傳入一個(gè)字符串到構(gòu)造函數(shù),然后提供了iterator方法對(duì)解析后的子串進(jìn)行遍歷,,是一個(gè)很常用的方法類(lèi),。


sql中帶有IN的子查詢(xún)綁定變量實(shí)現(xiàn)方式

在sql中經(jīng)常會(huì)帶有IN的子查詢(xún),如 where id in (1,2,3),。如果這樣的語(yǔ)句在數(shù)據(jù)庫(kù)中出現(xiàn),,將引起數(shù)據(jù)庫(kù)的大量硬解析與共享池SQL碎片,

下面介紹種將這些In list給綁定起來(lái):
1:首先針對(duì)數(shù)據(jù)類(lèi)型和字符類(lèi)型的綁定創(chuàng)建兩個(gè)類(lèi)型,。
create or replace type numtabletype as table of number;
create or replace type vartabletype as table of varchar2(1000);

2:創(chuàng)建兩個(gè)函數(shù),,分別來(lái)解析In list中的串

CREATE OR REPLACE  FUNCTION str2numList(p_string IN varchar2) RETURN numtabletype
AS
  v_str LONG DEFAULT p_string || ',';
  v_n NUMBER;
  v_data numtabletype := numtabletype();
BEGIN
  LOOP
    v_n := to_number(instr(v_str, ','));
    EXIT WHEN (nvl(v_n, 0) = 0);
    v_data.extend;
    v_data(v_data.count) := ltrim(rtrim(substr(v_str,1,v_n-1)));
    v_str := substr(v_str, v_n+1);
  END LOOP;
  
  RETURN v_data;
END;

CREATE OR REPLACE  FUNCTION str2varList(p_string IN varchar2) RETURN vartabletype
AS
  v_str LONG DEFAULT p_string || ',';
  v_n varchar2(2000);
  v_data vartabletype := vartabletype();
BEGIN
  LOOP
    v_n := to_number(instr(v_str, ','));
    EXIT WHEN (nvl(v_n, 0) = 0);
    v_data.extend;
    v_data(v_data.count) := ltrim(rtrim(substr(v_str,1,v_n-1)));
    v_str := substr(v_str, v_n+1);
  END LOOP;
  
  RETURN v_data;
END;

3:使用方式
添加Hint為了穩(wěn)定執(zhí)行計(jì)劃
SELECT /*+ ordered user_nl(a,b) */ b.* FROM TABLE(str2numList(:bind0)) a, ACCOUNT b WHERE b.id=a.column_value;
  SELECT /*+ ordered user_nl(a,b) */ b.* FROM TABLE(str2numList('1,2,3')) a, ACCOUNT b WHERE b.id=a.column_value;

SELECT /*+ leading(a) */ * FROM ACCOUNT WHERE ID IN (SELECT * FROM TABLE(str2numList(:bind1)) a);
SELECT /*+ leading(a) */ * FROM ACCOUNT WHERE ID 

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多