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比較: 2).和hibernate比較: 4. jdbc編程步驟回顧
5. jdbc問(wèn)題總結(jié)
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.xml8-2.4.使用junit進(jìn)行測(cè)試 9.MyBatis配置(第二種總結(jié)方式)全局配置文件:sqlmapconfig.xml文件中的配置項(xiàng)是有順序的,按照官方圖來(lái)配. 例如: settings: 全局參數(shù)設(shè)置 typeAliases:類(lèi)型別名 mappers: 映射文件注意點(diǎn)
parameterType:表示輸入?yún)?shù)的數(shù)據(jù)類(lèi)型 核心API執(zhí)行流程: SqlSessionFactoryBuilder案例 SqlSessionFactory創(chuàng)建SqlSession的方法: 用openSession方法獲取的SqlSession有以下特點(diǎn): 列舉3個(gè)執(zhí)行類(lèi)型參數(shù)(ExecutorType): SqlSessionSqlSession執(zhí)行增刪改查以及事務(wù)操作. 10.Dao開(kāi)發(fā)(第二種總結(jié)方式)實(shí)現(xiàn)方式: 傳統(tǒng)的Dao
接口: 實(shí)現(xiàn)類(lèi): 優(yōu)化: mapper動(dòng)態(tài)代理的方式mapper代理的方式,只需要寫(xiě)接口,不需要寫(xiě)實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)類(lèi)由mybatis框架自動(dòng)創(chuàng)建. 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和resultTypeparameterType:指定輸入?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和selectListselectOne查詢(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í)緩存的配置 2. 在mapper映射文件中開(kāi)啟二級(jí)緩存 3. 禁止使用緩存 18.MappersMapper是個(gè)接口,里面定義方法匹配SqlSession中方法. Mapper注解,參考Mybatis官方文檔. 19.動(dòng)態(tài)sqlMyBatis使用OGNL表達(dá)式,來(lái)支持一些常用的標(biāo)簽. 案例: ifchoose, when, otherwisetrim, where, setforeachbind創(chuàng)建一個(gè)ognl表達(dá)式,用于語(yǔ)句中. Multi-db vendor supportPluggable Scripting Languages For Dynamic SQL為動(dòng)態(tài)sql定制可插拔腳本語(yǔ)句 使用步驟: 再mybatis-config.xml或sqlmapconfig.xml文件中覆蓋默認(rèn)別名,使用自定義 在映射文件中使用 也可以在mapper中使用 sql片段抽取sql重復(fù)代碼,,提供效率,。 20.多表查詢(xún)輸出結(jié)果類(lèi)型
ResultMapresultType使用時(shí),設(shè)置值時(shí)需要查詢(xún)的列名和java對(duì)象屬性名一致.如果不一致的時(shí)候,可以使用resultMap.使用時(shí)需要先定義,再使用. 映射一對(duì)一關(guān)系 映射一對(duì)多關(guān)系 多對(duì)多映射 21.mybatis和spring的整合整合的步驟 4). spring的配置文件 5). 數(shù)據(jù)庫(kù)、日志的配置文件 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é)重載。 3.盡量少用if choose等語(yǔ)句,降低維護(hù)的難度,。 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ì)的。 4.用XML注釋取代SQL注釋,。 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中設(shè)計(jì)模式: Mybatis至少遇到了以下的設(shè)計(jì)模式的使用:
接下來(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)型模式,。 代理模式包含如下角色:
這里有兩個(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),使用了不同的策略:
比如在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包中):
另外,,還有一個(gè)特殊的裝飾器TransactionalCache:事務(wù)性的緩存 正如大多數(shù)持久層框架一樣,mybatis緩存同樣分為一級(jí)緩存和二級(jí)緩存
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): |
|
來(lái)自: Java幫幫 > 《待分類(lèi)》