2018-10-02
<mapper namespace="com.dao.AdministratorDao">
<insert id="insertInfo" useGeneratedKeys="true" keyProperty="id">
insert into administrator(id,name,age,job) VALUES
(#{id},#{administrator.name},${administrator.age},#{administrator.job})
</insert>
</mapper>
只有insert和update標(biāo)簽中有useGenaratedKeys和keyProperty兩個(gè)屬性鳞芙,這兩個(gè)屬性是為了獲取
數(shù)據(jù)庫中自增字段用的,將返回的值復(fù)制給keyProperty中設(shè)置的變量怎爵,然后可以在下面的sql語句這樣
使用悯森,你發(fā)現(xiàn),我的administrator只有插入了三個(gè)屬性巷怜,id屬性是沒有插入的葛超,這個(gè)屬性是為了接收
用的,也就是說在執(zhí)行完這條語句后延塑,我調(diào)用administrator.getId()會(huì)獲取到數(shù)據(jù)庫自增字段id的值绣张。
參考鏈接:https://blog.csdn.net/hellostory/article/details/6790248
對(duì)于不支持自動(dòng)生成主鍵的JDBC驅(qū)動(dòng)或者數(shù)據(jù)庫,需要在insert標(biāo)簽中加上標(biāo)簽<selectKey />
例如:
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
其中屬性值order需要注意关带,這個(gè)屬性值只有兩個(gè)值侥涵,一個(gè)是BEFORE,另一個(gè)是AFTER
如果設(shè)置為 BEFORE宋雏,那么它會(huì)首先選擇主鍵独令,設(shè)置 keyProperty 然后執(zhí)行插入語句。
如果設(shè)置為 AFTER好芭,那么先執(zhí)行插入語句燃箭,然后是 selectKey 元素
<sql>標(biāo)簽可以被用來定義可重用的SQL代碼段,可以包含在其他語句中
<include></include>標(biāo)簽里有個(gè)標(biāo)簽<property>
使用這個(gè)標(biāo)簽的時(shí)候要注意
我們知道在include標(biāo)簽中有個(gè)屬性refid用來指向一個(gè)sql標(biāo)簽的
那么這個(gè)property標(biāo)簽則是對(duì)sql標(biāo)簽中的值進(jìn)行復(fù)制用的舍败,我們來看下例子:
<select id="selectAll" resultType="Administrator">
select * from administrator
where job = <include refid="job"><property name="tt" value="程序員" /></include>
</select>
<sql id="job">
${tt}
</sql>
這條語句返回結(jié)果是錯(cuò)的招狸,它顯示找不到程序員這行,
我們?cè)谝谜Z句左右兩邊都加上引號(hào)邻薯,現(xiàn)在我們來看下輸出的sql語句:
select * from administrator where job = " 程序員 "
發(fā)現(xiàn)了吧裙戏,我們傳入值的左右兩邊都有空格,也就是說厕诡,在使用字段是字符串的情況下累榜,不能這樣寫,否則會(huì)出現(xiàn)上面的情況灵嫌。
#{變量名壹罚,屬性:xxxx,屬性:xxx}
關(guān)于使用和不使用@Param()注釋的區(qū)別
1、不使用@Param注釋寿羞,參數(shù)只能有一個(gè)猖凛,并且參數(shù)是對(duì)象,這樣使用#{}和使用{}取不到值
3第岖、使用@Param注釋,#{}和${}都可以取到值试溯,并且單個(gè)參數(shù)和多個(gè)參數(shù)的情況都適用
2018-10-09
以作用域+返回值+絕對(duì)路徑的方法名
e.g:
public abstract java.until.List com.dao.PersonDao.select_PersonAndJob() 這個(gè)作為key
將MapperMethod作為value
MapperMethod中有兩個(gè)比較重要的內(nèi)部類
1蔑滓、sqlCommand //封裝了SQL標(biāo)簽的類型:insert update delete select
2、MethodSignature //封裝了方法的參數(shù)信息 返回類型信息等
mybatis緩存
1耍共、一級(jí)緩存是通過SqlSession來實(shí)現(xiàn)的
圖片來自鏈接:https://blog.csdn.net/qq_25689397/article/details/52066179
這是只有mybatis框架的情況下烫饼,緩存的大體過程
如果只有mybatis的時(shí)候猎塞,當(dāng)進(jìn)入到MapperMethod類中調(diào)用selectList方法的時(shí)候试读,進(jìn)入的是DefaultSqlSession類
然后在這個(gè)類中調(diào)用執(zhí)行器executor的query方法,在執(zhí)行器中有個(gè)成員變量用于存儲(chǔ)緩存荠耽,就是PerpetualCache類
這個(gè)類中的成員變量cache存儲(chǔ)緩存钩骇,這個(gè)成員變量類型是hashmap類型,也就是mybatis的緩存數(shù)據(jù)是存儲(chǔ)在hashmap中的
注意:獲取mapperMethod
將路徑作為key铝量,通過方法cachedMapperMethod()方法來操作倘屹,如果key存在則直接取出MapperMethod對(duì)象
如果key不存在則新建一個(gè)mapperMethod對(duì)象,并將對(duì)象存入ConcurrentHashMap集合中慢叨,然后返回該對(duì)象纽匙。
接著是調(diào)用mapperMethod的execute()方法,根據(jù)mapperMethod的command的屬性類型拍谐,進(jìn)行相對(duì)應(yīng)的操作烛缔,
基本就是sqlSession的四個(gè)操作:insert update delete select
下面以select為例
調(diào)試進(jìn)入這個(gè)方法發(fā)現(xiàn)是進(jìn)入SqlSessionTemplate類,因?yàn)槲沂钦虾玫腟SM框架轩拨,所以在這個(gè)時(shí)候是spring在
對(duì)這些sqlSession進(jìn)行代理操作践瓷,在調(diào)用這個(gè)方法的時(shí)候,會(huì)進(jìn)入到這個(gè)類的內(nèi)部類SqlSessionInterceptor
在這個(gè)類invoke方法中亡蓉,我們發(fā)現(xiàn)晕翠,他在finally{}代碼塊中對(duì)sqlSession進(jìn)行了關(guān)閉操作,這也是為什么我們
每一次調(diào)用sql語句砍濒,不是從緩存中取出來淋肾,而是直接跟數(shù)據(jù)庫打交道,也就是每個(gè)session運(yùn)行完一個(gè)方法后爸邢,就
會(huì)被銷毀巫员,即創(chuàng)建快銷毀的也快
2、二級(jí)緩存是通過mapper的namespace實(shí)現(xiàn)共享
也就是不同的session對(duì)于namespace的操作是共享的甲棍,每個(gè)namespace都有一塊緩存简识,在執(zhí)行查詢的時(shí)候默認(rèn)不刷新緩存赶掖,
而在執(zhí)行增刪改的時(shí)候是默認(rèn)要刷新緩存的,mybatis提供給我們兩個(gè)屬性進(jìn)行設(shè)置七扰,一個(gè)是useCache一個(gè)是flushCache
并且默認(rèn)值如下:
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>
增刪改是沒有useCache屬性的奢赂,被定死了
mybatis本身的二級(jí)緩存很容易出現(xiàn)問題,也就是到多表操作的時(shí)候颈走,容易出現(xiàn)臟數(shù)據(jù)的問題膳灶,而且mybatis本身的二級(jí)緩存
無法實(shí)現(xiàn)分布式具體的問題,可以查看:https://www.cnblogs.com/liouwei4083/p/6025929.html
基于此立由,mybatis給我們提供了可以自定義的二級(jí)緩存轧钓,也就是添加了Cache接口,實(shí)現(xiàn)這個(gè)接口锐膜,可以自己來實(shí)現(xiàn)緩存毕箍,
通常可以借助實(shí)現(xiàn)這個(gè)接口來使用redis做mybatis的二級(jí)緩存道盏,然后再在配置文件里的<cache />標(biāo)簽里添加這個(gè)類的路徑而柑,
這樣便可以實(shí)現(xiàn)分布式。
SSM框架整合后荷逞,我們看下調(diào)試過程
我們看下代碼的實(shí)現(xiàn)媒咳,首先進(jìn)入MapperProxy類,也就是spring代理類
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
這個(gè)方法中种远,最重要的是最后兩行的代碼涩澡,cachedMapperMethod()方法是判斷當(dāng)前方法路徑下是否有緩存,有緩存則將緩存返回坠敷,沒有緩存妙同,則重新創(chuàng)建一個(gè)MapperMethod對(duì)象,并且將對(duì)象存入ConcurrentHashMap中常拓,這也是跟單獨(dú)使用mybatis的區(qū)別之一渐溶,存儲(chǔ)容器不同。然后通過MapperMethod的方法execute()執(zhí)行sql語句弄抬。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根據(jù)sql的類型茎辐,選擇不同的執(zhí)行方式
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//當(dāng)前方法調(diào)試的入口
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
我們可以看到不管是增刪改查哪一個(gè)都會(huì)調(diào)用sqlSession對(duì)應(yīng)的方法,程序進(jìn)一步進(jìn)行掂恕,會(huì)到類SqlSessionTemplate類拖陆,調(diào)用selectList()方法,在調(diào)用這個(gè)方法的時(shí)候,會(huì)先進(jìn)入到這個(gè)類的內(nèi)部類SqlSessionInterceptor中懊亡,也就是Session的攔截器依啰,調(diào)用了攔截器的Invoke方法,我們看下這個(gè)方法的實(shí)現(xiàn):
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
//注意每次SqlSession操作數(shù)據(jù)庫后店枣,都會(huì)自動(dòng)斷開速警,這也是為什么mybatis一級(jí)緩存會(huì)失效的原因
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
看到這塊基本就能了解叹誉,為什么mybatis的一級(jí)緩存在SSM框架中會(huì)失效了,也就是spring管理使用sqlSession后闷旧,每次使用都會(huì)關(guān)閉长豁,也就是sqlSession的創(chuàng)建和關(guān)閉變得頻繁了
參考鏈接:
https://www.cnblogs.com/volatileAndCrazy/p/7826331.html
https://blog.csdn.net/z69183787/article/details/78402892
https://www.cnblogs.com/winclpt/articles/7511672.html
https://blog.csdn.net/qq_25689397/article/details/52066179
https://www.cnblogs.com/linkstar/p/7182695.html