使用原生jdbc的問(wèn)題
- 數(shù)據(jù)庫(kù)連接摧玫, 使用時(shí)就創(chuàng)建拌夏,不使用就立即釋放赵辕,對(duì)數(shù)據(jù)庫(kù)進(jìn)行頻繁地鏈接開(kāi)啟和關(guān)閉,造成數(shù)據(jù)庫(kù)資源浪費(fèi)身辨,影響數(shù)據(jù)庫(kù)性能丐谋。
解決方案: 使用數(shù)據(jù)庫(kù)連接池管理數(shù)據(jù)庫(kù)連接。
2.將sql語(yǔ)句硬編碼到j(luò)ava代碼中栅表,如果sql語(yǔ)句修改笋鄙,需要重新編譯java代碼,不利于系統(tǒng)維護(hù)怪瓶。
設(shè)想:將sql 語(yǔ)句配置到xml文件中萧落。
3.向preparedStatement中設(shè)置參數(shù),設(shè)置占位符號(hào)位置洗贰,以及對(duì)應(yīng)的參數(shù)值屬于硬編碼找岖,不利于系統(tǒng)維護(hù)。
設(shè)想 將sql語(yǔ)句及占位符號(hào)和全部參數(shù)配置在xml中敛滋。
4.從resultset中遍歷結(jié)果集數(shù)據(jù)時(shí)存在硬編碼许布,將獲取表的字段進(jìn)行成硬編碼,不利于系統(tǒng)維護(hù)绎晃。
設(shè)想:將查詢(xún)結(jié)果集蜜唾,自動(dòng)映射成java對(duì)象杂曲。
mybatis和hibernate的區(qū)別和應(yīng)用場(chǎng)景
hibernate:是一個(gè)ORM框架(對(duì)象關(guān)系映射),不需要程序?qū)憇ql, sql語(yǔ)句自動(dòng)生成了袁余。
對(duì)sql語(yǔ)句進(jìn)行優(yōu)化擎勘、修改比較困難。
mybatis:專(zhuān)注sql本身颖榜,需要程序員自己編寫(xiě)sql語(yǔ)句棚饵,sql修改、優(yōu)化比較方便掩完。mybatis是一個(gè)不完全的ORM框架噪漾,雖然程序員自己寫(xiě)sql,mybatis也可以實(shí)現(xiàn)映射(輸入映射、輸出映射)且蓬。
應(yīng)用場(chǎng)景:
適用于需求變化比較多的項(xiàng)目欣硼,比如:互聯(lián)網(wǎng)項(xiàng)目。
mybatis 開(kāi)發(fā)dao的方法
SqlSessionBuilder作為工具類(lèi)創(chuàng)建SqlSessionFactory
SqlSessionFactory使用單例模式
SqlSession是線程不安全的缅疟,在SqlSession實(shí)現(xiàn)類(lèi)中除了有接口中方法還有數(shù)據(jù)域?qū)傩浴?/p>
總結(jié)原始dao開(kāi)發(fā)問(wèn)題
- statement 的id需要在代碼中調(diào)用分别,屬于硬編碼
- 調(diào)用sqlsession方法時(shí)傳入的變量,由于sqlsession方法使用泛型存淫,即使變量類(lèi)型傳入錯(cuò)誤,在編譯階段也不報(bào)錯(cuò)
- dao接口實(shí)現(xiàn)類(lèi)方法中存在大量模板方法沼填,設(shè)想能否將這些代碼提取出來(lái)桅咆。
mapper 代理方法
程序員只需要實(shí)現(xiàn)mapper接口(相當(dāng)于dao接口)
程序員還需要編寫(xiě)mapper.xml映射文件
需要遵循一個(gè)規(guī)范
- mapper.xml 中namespace 就是mapper.java 的全限定名
- mapper.xml 中statement 的id 就是 mapper.java 的方法名一致
- mapper.xml 中statement 的parameterType 指定輸入?yún)?shù)類(lèi)型和mapper.java的方法輸入,參數(shù)類(lèi)型一致坞笙。
- mapper.xml 中statement 的resultType 指定輸出結(jié)果的類(lèi)型和mapper.java的返回方法值類(lèi)型一致岩饼。
mapper 接口方法參數(shù)只能有一個(gè)是否影響系統(tǒng)開(kāi)發(fā)
mapper接口方法參數(shù)只能有一個(gè),系統(tǒng)是否不利于擴(kuò)展維護(hù)薛夜?
解決
系統(tǒng)框架中籍茧,dao層的代碼是被業(yè)務(wù)層公用的。
即使mapper接口只有一個(gè)參數(shù)梯澜,可以使用包裝類(lèi)型的pojo滿(mǎn)足不同的業(yè)務(wù)方法的需求寞冯。
注意
持久層方法參數(shù)可以包裝類(lèi)型,但是service 方法中建議不要使用包裝類(lèi)型(不利于業(yè)務(wù)層可擴(kuò)展性)晚伙。
將sql連接信息寫(xiě)入db.properties文件中
mybatis 配置
typeAliases
用于配置單個(gè)類(lèi)的別名或者整個(gè)包的別名
typeHandlers
實(shí)現(xiàn)java類(lèi)和jdbc類(lèi)型的相互轉(zhuǎn)化吮龄,由mybatis的一系列類(lèi)型handler實(shí)現(xiàn)。
mapper 配置
單個(gè)映射文件加載使用
<mapper class="com.vee.mybatis.mapper.UserMapper"></mapper>
批量加載
<package name="com.vee.mybatis.mapper"></package>
使用條件
使用package進(jìn)行批量加載,要求與使用class一樣需要將mapper接口和xml映射文件名稱(chēng)保持一致咆疗,且在一個(gè)目錄中漓帚。
Vo包裝pojo類(lèi)
- 通過(guò)創(chuàng)建子類(lèi)對(duì)pojo 類(lèi)進(jìn)行擴(kuò)展
- 創(chuàng)建vo類(lèi)包裝pojo類(lèi)可以實(shí)現(xiàn)多表聯(lián)合查詢(xún)
- 使用Vo 類(lèi)可以利用成員中的pojo類(lèi)或者pojo擴(kuò)展類(lèi),實(shí)現(xiàn)多參數(shù)查詢(xún)午磁。
resultType
使用resultType 進(jìn)行輸出映射尝抖,只要查詢(xún)出來(lái)的列名和Pojo中的屬性名一致毡们,該列才可以映射成功。
如果查詢(xún)出來(lái)的列名和pojo中的屬性名全部不一致昧辽,沒(méi)有創(chuàng)建pojo對(duì)象漏隐。
只要查詢(xún)出來(lái)的列名和pojo中的屬性有一個(gè)一致,就會(huì)創(chuàng)建pojo對(duì)象奴迅。
resultMap
如果查詢(xún)出來(lái)的列名和pojo的屬性名不一致青责,通過(guò)定義一個(gè)resultMap對(duì)列名和pojo屬性名之間作一個(gè)映射關(guān)系。
- 定義resultMap
- 使用resultMap作為statement的輸出映射類(lèi)型
動(dòng)態(tài)sql
mysql核心 對(duì)sql語(yǔ)句進(jìn)行靈活操作取具,對(duì)sql進(jìn)行靈活拼接組裝
例如:
對(duì)查詢(xún)條件進(jìn)行判斷脖隶, 如果輸入?yún)?shù)不為空才進(jìn)行查詢(xún)條件拼接。
使用它可以防止空指針異常
<select id="findUserList4" parameterType="UserQueryVo" resultType="UserCustom">
SELECT * FROM USER
<where>
<if test="userCustom.uid!=0">
AND uid = #{userCustom.uid}
</if>
<if test="userCustom.uname!=null">
AND uname LIKE '%${userCustom.uname}%'
</if>
</where>
</select>
sql 片段
將上邊實(shí)現(xiàn)的動(dòng)態(tài)sql 判斷代碼塊抽取出來(lái)暇检,組成一個(gè)sql 片段产阱。其他的statement中就可以引用sql片段。
- sql 片段可以提高sql代碼的可重用性
- sql 片段建議基于單表
- sql 片段不用包含where防止使用多個(gè)sql片段時(shí)where 重復(fù)
foreach
向sql傳遞數(shù)組或List块仆,mybatis 使用foreach解析
例如如下的sql語(yǔ)句
select * from user where id=1 or id=2 or id=10
或者
select * from user where id IN(1,2,10)
這就需要在輸入?yún)?shù)類(lèi)型中添加List<Integer> ids
<select id="findUserList6" parameterType="UserQueryVo2" resultType="UserCustom">
SELECT * FROM USER
<where>
<if test="userCustom!=null">
AND uname LIKE '%${userCustom.uname}%'
</if>
<if test="ids!=null">
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
uid=#{id}
</foreach>
</if>
</where>
</select>
一對(duì)一查詢(xún)
做法:
對(duì)需要用到的屬性較多的類(lèi)進(jìn)行擴(kuò)展构蹬,并將另一個(gè)類(lèi)的屬性作為字段傳入。
使用resultMap
在<resultMap>標(biāo)簽中使用<association>標(biāo)簽映射關(guān)聯(lián)查詢(xún)單個(gè)對(duì)象的信息,property:將要關(guān)聯(lián)查詢(xún)的用戶(hù)信息映射到Orders中哪個(gè)屬性悔据。
總結(jié):
resultType:如果沒(méi)有查詢(xún)結(jié)果的特殊要求建議使用resultType庄敛,如果pojo中沒(méi)有包括查詢(xún)出來(lái)的列名,需要在擴(kuò)展類(lèi)中增加列名對(duì)應(yīng)的屬性科汗。
resultMap:需要單獨(dú)定義resultMap,如果對(duì)查詢(xún)結(jié)果有特殊要求(列名和pojo中的屬性名稱(chēng)不匹配)可以使用resulMap對(duì)屬性名和列名進(jìn)行映射藻烤。
一對(duì)多查詢(xún)
做法:
- 使用resultType,創(chuàng)建新pojo包含必要屬性,然后返回pojo列表
- 使用resultMap头滔,擴(kuò)展類(lèi)添加List<>屬性,需要使用<resultMap>內(nèi)部標(biāo)簽中的<collection>標(biāo)簽怖亭。
多對(duì)多查詢(xún)
resultMap的可以在pojo中有List的時(shí)候在<resultMap>標(biāo)簽中定義collection,并且collection中可以遞歸定義collection坤检,但是這種場(chǎng)景往往使用resultType 兴猩,利用擴(kuò)展的pojo類(lèi)也可以很輕松地完成功能。
但是有的需求確實(shí)需要一個(gè)類(lèi)有一個(gè)list成員早歇,這個(gè)時(shí)候還是resultMap適合倾芝。
resultMap和resultType小結(jié)
resultType和resultMap都可以完成高級(jí)結(jié)果映射,如果沒(méi)有特殊要求缺前,使用resultType方便蛀醉,resultType和resultMap 的一個(gè)主要區(qū)別就是要應(yīng)用場(chǎng)景不同resultType主要是查詢(xún)明細(xì)使用,resultMap主要是層級(jí)查詢(xún)使用衅码,比如查詢(xún)用戶(hù)信息拯刁,如何點(diǎn)擊查詢(xún)訂單,再去查詢(xún)訂單信息逝段,resultMap可以實(shí)現(xiàn)延遲加載垛玻,而resultType沒(méi)有該功能割捅。
延遲加載
resultMap 可以實(shí)現(xiàn)高級(jí)映射(使用association、實(shí)現(xiàn)一對(duì)一和一對(duì)多映射)帚桩,association亿驾、collection具備延遲加載功能。
延遲加載:先從單表查詢(xún)账嚎、需要時(shí)再?gòu)年P(guān)聯(lián)表去關(guān)聯(lián)查詢(xún)莫瞬,大大提高數(shù)據(jù)庫(kù)性能。
使用resultMap 中的association 去實(shí)現(xiàn)延遲加載郭蕉。
select 屬性可以執(zhí)行statement的id
- mybatis默認(rèn)沒(méi)有開(kāi)啟延遲加載疼邀,需要在sqlMapConfig.xml中setting 配置。
設(shè)置項(xiàng) | 描述 | 允許值 | 默認(rèn)值 |
---|---|---|---|
lazyLoadingEnable | Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. | true/false | false |
aggressiveLazyLoading(不常用) | When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded on demand | true/false | false (true in ≤3.4.1) |
- 不使用延遲加載也是可以完成功能的召锈,延遲加載的本質(zhì)是延遲sql查詢(xún)的時(shí)間旁振,那么使用不同的mapper也可以完成先單表查詢(xún),然后一些屬性按需查詢(xún)的功能
resultType涨岁、resultMap拐袜、延遲加載使用場(chǎng)景總結(jié)
- 延遲加載:
mybatis 提供 的延遲加載功能用于service 層,要在對(duì)象消亡之前進(jìn)行 - resultType
將明細(xì)記錄映射到對(duì)應(yīng)的pojo中 - resultMap
使用association 和collection 完成一對(duì)一和一對(duì)多的高級(jí)映射 - association
將關(guān)聯(lián)查詢(xún)信息映射到一個(gè)pojo類(lèi)中梢薪。 - collection
將關(guān)聯(lián)查詢(xún)信息映射到到一個(gè)list集合中蹬铺。
查詢(xún)緩存
使用緩存可以將熱數(shù)據(jù)放在內(nèi)存中,用戶(hù)查詢(xún)數(shù)據(jù)就不用從磁盤(pán)上查詢(xún)沮尿,這樣可以提高查詢(xún)效率丛塌,解決了高并發(fā)系統(tǒng)的性能問(wèn)題。
mybatis中二級(jí)緩存生存與sqlSession中畜疾,二級(jí)緩存是mapper級(jí)別的緩存,二級(jí)緩存是可以被不同的sqlSession共享的印衔。
一級(jí)緩存
第一次查詢(xún)一個(gè)sql啡捶,sql 查詢(xún)結(jié)果寫(xiě)入sqlsession 緩存中,同一個(gè)sqlsession 再次發(fā)出相同的sql 奸焙,就從緩存中取瞎暑。如果兩次中間出現(xiàn)了commit曹組,本sqlsession 中的一級(jí)緩存區(qū)域就全部清空与帆。
mybatis默認(rèn)支持一級(jí)緩存了赌,但是mybatis 和spring整合后進(jìn)行mapper代理開(kāi)發(fā),不支持一級(jí)緩存玄糟。mybatis 和spring整合勿她,spring按照mapper模板去生成mapper代理對(duì)象,模板中在最后統(tǒng)一關(guān)閉了sqlsession阵翎。
二級(jí)緩存
二級(jí)緩存的范圍是mapper級(jí)別(mapper同一個(gè)命名空間)逢并,mapper以命名空間為單位創(chuàng)建緩存數(shù)據(jù)結(jié)構(gòu)之剧。
- mybatis開(kāi)啟二級(jí)緩存
<setting name="cacheEnabled" value="true">
還需要在mapper.xml中添加<cache />標(biāo)簽。 - 查詢(xún)結(jié)果映射的pojo需要序列化砍聊,實(shí)現(xiàn)java.io.serializable接口背稼。
二級(jí)緩存可以將內(nèi)存的數(shù)據(jù)寫(xiě)到磁盤(pán),存在對(duì)象的序列化和反序列化玻蝌,所以要實(shí)現(xiàn)java.io.serializable 接口蟹肘。 - 一個(gè)sqlSession 在close的時(shí)候會(huì)將緩存寫(xiě)入到公有緩存(磁盤(pán)上)。
- 單個(gè)statement 禁止使用二級(jí)緩存俯树。
<select id="findUserList6" parameterType="UserQueryVo" resultType="UserCustom" useCache="false">
對(duì)于變化頻率較高的select帘腹,需要禁用二級(jí)緩存,即該sql使用二級(jí)緩存聘萨。 - 如果sqlSession進(jìn)行commit操作竹椒,刷新緩存(全局清空)。
設(shè)置statement 的flushCache 是否刷新緩存米辐,默認(rèn)值是true胸完,設(shè)置為false可能導(dǎo)致臟讀。