序言:
文章內(nèi)容輸出來源:拉勾教育Java高薪訓(xùn)練營。
本篇文章是學(xué)習(xí)課程中的一部分課后筆記
一河质、自定義持久層框架Ipersistence是如何解決JDBC存在的問題?
- 使用配置文件解決硬編碼問題
- 使用C3P0連接池解決了頻繁創(chuàng)建釋放數(shù)據(jù)庫連接問題
- 在simpleExecute中使用了反射進(jìn)行了參數(shù)的設(shè)置
- 在simpleExecute中使用了內(nèi)省進(jìn)行返回結(jié)果集的封裝
二嘲碱、進(jìn)行自定義持久層框架Ipersistence優(yōu)化時(shí)主要為了解決哪些問題累奈?
- 應(yīng)用在Dao層,整個(gè)操作的模板代碼重復(fù)
- 調(diào)用sqlSession方法時(shí)彻桃、參數(shù)statementId硬編碼
三坛善、關(guān)于configuration 與 mappedStatement配置類的說法:
- 使用dom4j對(duì)sqlMapConfig.xml解析時(shí)會(huì)將解析出來的內(nèi)容以不同形式封裝到Configuration對(duì)象中
- 使用dom4j對(duì)mapper.xml解析時(shí),每一個(gè)<select>標(biāo)簽的內(nèi)容均對(duì)應(yīng)一個(gè)mappedStatement對(duì)象
- configuration 中包含了mappedStatement的引用
四邻眷、關(guān)于genericTokenParser類的說法:
- 該類只能通過有參構(gòu)造進(jìn)行創(chuàng)建
- genericTokenParser是通用的標(biāo)記解析類
- genericTokenParser在解析#{}占位符時(shí)必須通過標(biāo)記處理類tokenHandler的配合
- genericTokenParser三個(gè)構(gòu)造參數(shù)分別為開始標(biāo)記眠屎、結(jié)束標(biāo)記、標(biāo)記處理器
五肆饶、關(guān)于sqlessionFactoyBuilder改衩,sqlessionFactoy ,sqlession的說法:
- sqlessionFactoyBuilder最佳范圍為方法范圍驯镊,可定義為本地方法變量
- sqlessionFactoy 最佳范圍是應(yīng)用范圍
- sqlession最佳范圍是方法范圍或請(qǐng)求范圍
六葫督、關(guān)于resultType與入?yún)⒌恼f法:
- resultType表示返回值類型為:完整類名或別名竭鞍,允許使用基本數(shù)據(jù)類型,String橄镜、int等
- resultType和resultMap的數(shù)據(jù)結(jié)構(gòu)一樣的偎快,都是map結(jié)構(gòu)
- mybatis中,除了使用@param注解來實(shí)現(xiàn)多個(gè)參數(shù)入?yún)⑶⒔海€可以用Map對(duì)象實(shí)現(xiàn)多參數(shù)傳遞
七晒夹、mybatis中接口開發(fā)規(guī)范:
- mapper.xml中的namespace與mapper接口的類路徑相同
- mapper接口方法名和mapper.xml中定義的每個(gè)statement的id相同
- mapper接口方法的輸入?yún)?shù)類型和mapper.xml中定義的每個(gè)sql的parameterType類型相同
- mapper接口方法的輸出參數(shù)類型和mapper.xml中定義的每個(gè)sql的resultType類型相同
八、關(guān)于mybatis源碼內(nèi)容的說法:
- 涉及到的設(shè)計(jì)模式有:代理模式姊氓、builder模式丐怯、工廠模式、迭代器模式
- 功能架構(gòu)可以分為三層:接口層他膳、數(shù)據(jù)處理層响逢、框架支撐層
- 支持用插件對(duì)statementHandler、paramterHandle棕孙、resultsetHandler等核心對(duì)象進(jìn)行攔截
- Executor是執(zhí)行器,負(fù)債sql的生成和查詢緩存的維護(hù)
- statementtHandler封裝了jdbc statement操作些膨,負(fù)責(zé)對(duì)jdbc statement的操作
- typeHandler 負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的映射和轉(zhuǎn)換
- sqlSource 負(fù)責(zé)根據(jù)用戶傳遞的parameterObject動(dòng)態(tài)生產(chǎn)sql語句蟀俊,將信息封裝到boundsql對(duì)象中
九、Mybatis動(dòng)態(tài)sql是做什么的订雾?都有哪些動(dòng)態(tài)sql肢预?簡述一下動(dòng)態(tài)sql的執(zhí)行原理?
1.動(dòng)態(tài)sql:
- 在數(shù)據(jù)庫執(zhí)行sql之前洼哎,需要對(duì)sql進(jìn)行整合處理烫映,根據(jù)傳入的參數(shù)值,以及匹配的條件噩峦,有可能需要?jiǎng)討B(tài)的去
判斷是否為空锭沟,拼接sql。
2.動(dòng)態(tài)sql:
<where> 识补、 <if> 族淮、<include> 、<foreach>
<select id="selectUserByCondition" resultType="com.admin.entity.User">
select
<include refid="userInfo"/>
from
user
<where>
<if test="name !=null and name !=''">
and name = #{name }
</if>
<if test="roleIdList!=null and roleIdList.size() > 0 ">
and role_id in
<foreach collection="roleIdList" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</where>
</select>
map 入?yún)?/strong>
<select id="selectByMap" resultType="com.admin.entity.User">
select * from user where
<foreach collection="map" index="key" item="value" separator="=">
${key} = #{value}
</foreach>
</select>
<choose>凭涂、 <when>祝辣、 <otherwise>
<select id="selectUserInfo" resultType="com.admin.entity.User">
select * from user
<where>
<choose>
<when test="name != null and name != ''">
and name = #{name }
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</where>
</select>
<set>、 <trim>
<update id="updateUser" parameterType="com.admin.entity.User">
update user
<set>
<if test="name != null and name != ''">
name = #{name },
</if>
</set>
where id = #{id}
</update>
<select id="selectUserByTrim" resultType="com.admin.entity.User">
select * from user
<trim prefix="where" suffix="order by id" prefixOverrides="and | or" suffixOverrides=",">
<if test="name != null and name != ''">
and name = #{name }
</if>
<if test="id != null and id !='' ">
and id = #{id}
</if>
</trim>
</select>
-- 批量更新
<update id="updateUserInfoByList" parameterType="java.util.List">
update user
<trim prefix="set" suffixOverrides=",">
<trim prefix="name =case" suffix="end,">
<foreach collection="userList" item="item">
<if test="item.name!=null">
when id=#{item.id} then #{item.name}
</if>
</foreach>
</trim>
<trim prefix="role_id =case" suffix="end,">
<foreach collection="userList" item="item">
<if test="item.roleId!=null and item.roleId!=''">
when id=#{item.id} then #{item.roleId}
</if>
</foreach>
</trim>
</trim>
<where>
<foreach collection="userList" separator="or" item="item">
id = #{item.id}
</foreach>
</where>
</update>
3.動(dòng)態(tài)sql的執(zhí)行原理:
- MyBatis的動(dòng)態(tài)SQL是基于OGNL表達(dá)式切油。
- 在解析xml配置文件的時(shí)候蝙斜,會(huì)有一個(gè)createSqlSource的操作。
- createSqlSource底層使用了XMLScriptBuilder調(diào)用parseScriptNode()的方法澎胡,解析xml的標(biāo)簽孕荠。
- 在parseScriptNode()的方法中有一個(gè)parseDynamicTags()方法娩鹉,會(huì)對(duì)nodeHandlers里的標(biāo)簽根據(jù)不同的handler來處理不同的標(biāo)簽 , 然后把DynamicContext結(jié)果放回SqlSource中岛琼。
- 在Executor執(zhí)行的時(shí)候底循,調(diào)用DynamicSqlSource的解析方法,并返回解析好的BoundSql槐瑞,排好序熙涤,替換好參數(shù)的sql。
十困檩、Mybatis是否支持延遲加載祠挫?如果支持,它的實(shí)現(xiàn)原理是什么悼沿?
1.Mybatis支持延遲加載
- 僅支持 association 關(guān)聯(lián)對(duì)象和 collection 關(guān)聯(lián)集合對(duì)象的延遲加載等舔;
- association 指的是一對(duì)一,collection 指的是一對(duì)多查詢糟趾。
- 配置文件中慌植,可以配置是否啟用延遲加載 lazyLoadingEnabled=true|false。
<settings>
<!-- 打開延遲加載的開關(guān) -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 將積極加載改為消極加載义郑,即延遲加載 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2.實(shí)現(xiàn)原理
- 使用 CGLIB 創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象蝶柿,當(dāng)調(diào)用目標(biāo)方法時(shí),進(jìn)入攔
截器方法非驮,當(dāng)數(shù)據(jù)需要的時(shí)候在調(diào)用sql查詢DB交汤。 - 例如 : 調(diào)用 a.getB().getName(),攔截器 invoke()方法發(fā)現(xiàn) a.getB()是
null 值劫笙,那么就會(huì)單獨(dú)發(fā)送事先保存好的查詢關(guān)聯(lián) B 對(duì)象的 sql芙扎,把 B 查詢上來,
然后調(diào)用 a.setB(b)填大,于是 a 的對(duì)象 b 屬性就有值了戒洼,接著完成 a.getB().getName()方法的調(diào)用。
十一栋盹、Mybatis都有哪些Executor執(zhí)行器施逾?它們之間的區(qū)別是什么?
1.三種Executor:
- SimpleExecutor 例获、ReuseExecutor 汉额、 BatchExecutor
2.區(qū)別
SimpleExecutor:每執(zhí)行一次update或select,就開啟一個(gè)Statement對(duì)象榨汤,用完立刻關(guān)閉Statement對(duì)象蠕搜。
ReuseExecutor:執(zhí)行update或select,以sql作為key查找Statement對(duì)象收壕,存在就使用妓灌,不存在就創(chuàng)建轨蛤,用完后,不關(guān)閉Statement對(duì)象虫埂,而是放置于Map內(nèi)祥山,供下一次使用。
簡言之掉伏,就是重復(fù)使用Statement對(duì)象缝呕。BatchExecutor:執(zhí)行update(沒有select,JDBC批處理不支持select)斧散,將所有sql都添加到批處理中addBatch()供常,等待統(tǒng)一執(zhí)行executeBatch(),它緩存了多個(gè)Statement對(duì)象鸡捐,每個(gè)Statement對(duì)象都是addBatch()完畢后栈暇,等待逐一執(zhí)行executeBatch()批處理荤懂。
與JDBC批處理相同嗓蘑。作用范圍:Executor的這些特點(diǎn),都嚴(yán)格限制在SqlSession生命周期范圍內(nèi)墓塌。
在Mybatis配置文件中色迂,可以指定默認(rèn)的ExecutorType執(zhí)行器類型新博,也可以手動(dòng)給DefaultSqlSessionFactory的創(chuàng)建SqlSession的方法傳遞ExecutorType類型參數(shù)。
十二脚草、簡述下Mybatis的一級(jí)、二級(jí)緩存(分別從存儲(chǔ)結(jié)構(gòu)原献、范圍馏慨、失效場(chǎng)景。三個(gè)方面來作答)姑隅?
1.存儲(chǔ)結(jié)構(gòu):
- 一級(jí)緩存写隶、二級(jí)緩存都是緩存到HashMap結(jié)構(gòu)中。
2.范圍:
一級(jí)緩存是SqlSession級(jí)別的讲仰,作用域是SqlSession慕趴,Mybatis默認(rèn)開啟一級(jí)緩存,在同一個(gè)SqlSession中鄙陡,相同的Sql查詢的時(shí)候冕房,第一次查詢的時(shí)候,就會(huì)從緩存中取趁矾,如果發(fā)現(xiàn)沒有數(shù)據(jù)耙册,那么就從DB查詢出來,并且緩存到HashMap中毫捣,第二次查詢直接在緩存中取详拙,有數(shù)據(jù)就直接返回帝际,不查DB。
二級(jí)緩存是mapper級(jí)別的饶辙,多個(gè)SqlSession去操作同一個(gè)mapper的sql語句蹲诀,多個(gè)SqlSession可以共用二級(jí)緩存,二級(jí)緩存是跨SqlSession弃揽。
第一次調(diào)用mapper下的sql 的時(shí)候去查詢信息脯爪,查詢到的信息會(huì)存放到該mapper對(duì)應(yīng)的二級(jí)緩存區(qū)域,第二次調(diào)用namespace下的mapper映射文件中蹋宦,相同的SQL去查詢披粟,直接在二級(jí)緩存內(nèi)取結(jié)果。
3.失效場(chǎng)景:
一級(jí)緩存 當(dāng)進(jìn)行增刪改的操作的時(shí)候冷冗,緩存將會(huì)失效守屉。
在spring容器管理中每次查詢都是創(chuàng)建一個(gè)新的sqlSession,所以在分布式環(huán)境中不會(huì)出現(xiàn)數(shù)據(jù)不一致的問題蒿辙。二級(jí)緩存 使用時(shí)需要開啟cache標(biāo)簽拇泛,在select上添加useCache屬性為true,
在更新和刪除時(shí)候需要手動(dòng)開啟flushCache刷新緩存思灌。
十三俺叭、簡述Mybatis的插件運(yùn)行原理,以及如何編寫一個(gè)插件泰偿?
1.運(yùn)行原理:
- mybatis可以編寫針對(duì)Executor熄守、StatementHandler、ParameterHandler耗跛、ResultSetHandler四個(gè)接口的插件裕照,mybatis使用JDK的動(dòng)態(tài)代理為需要攔截的接口生成代理對(duì)象,然后實(shí)現(xiàn)接口的攔截方法调塌,所以當(dāng)執(zhí)行需要攔截的接口方法時(shí)晋南,會(huì)進(jìn)入攔截方法(AOP思想)。
2.如何編寫:
1.編寫Intercepror接口的實(shí)現(xiàn)類
2.設(shè)置插件的簽名羔砾,告訴mybatis攔截哪個(gè)對(duì)象的哪個(gè)方法
3.最后將插件注冊(cè)到全局配置文件中
示例:
//插件簽名负间,告訴mybatis當(dāng)前插件攔截哪個(gè)對(duì)象的哪個(gè)方法
//type表示要攔截的目標(biāo)對(duì)象,method表示要攔截的方法姜凄,args表示要攔截方法的參數(shù)
@Intercepts({
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor {
//攔截目標(biāo)對(duì)象的目標(biāo)方法執(zhí)行
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("MySecondPlugin攔截目標(biāo)對(duì)象:"+invocation.getTarget()+"的目標(biāo)方法:"+invocation.getMethod());
/*
* 插件的主要功能:在執(zhí)行目標(biāo)方法之前政溃,可以對(duì)sql進(jìn)行修改已完成特定的功能
* 例如增加分頁功能,實(shí)際就是給sql語句添加limit檀葛;還有其他等等操作都可以
* */
//執(zhí)行目標(biāo)方法
Object proceed = invocation.proceed();
//返回執(zhí)行后的返回值
return proceed;
}
//包裝目標(biāo)對(duì)象:為目標(biāo)對(duì)象創(chuàng)建代理對(duì)象
@Override
public Object plugin(Object target) {
System.out.println("MySecondPlugin為目標(biāo)對(duì)象"+target+"創(chuàng)建代理對(duì)象");
//this表示當(dāng)前攔截器玩祟,target表示目標(biāo)對(duì)象,wrap方法利用mybatis封裝的方法為目標(biāo)對(duì)象創(chuàng)建代理對(duì)象(沒有攔截的對(duì)象會(huì)直接返回屿聋,不會(huì)創(chuàng)建代理對(duì)象)
Object wrap = Plugin.wrap(target, this);
return wrap;
}
//設(shè)置插件在配置文件中配置的參數(shù)值
@Override
public void setProperties(Properties properties) {
System.out.println("MySecondPlugin配置的參數(shù):"+properties);
}
}
- 插件注冊(cè)到全局配置文件中
<plugins>
<plugin interceptor="com.mybatis_demo.plugin.MySecondPlugin"></plugin>
</plugins>