1、 區分 #{} 和 ${}的不同應用場景
1)#{} 會生成預編譯SQL,會正確的處理數據的類型,而${}僅僅是文本替換。
對於SQL: select * from student where xCode = ‘S123456’;
如果使用#{}
那么生成的SQL為:
select * from student where xCode = ? 傳的值為’S123456’;
如果使用${}
那么生成的SQL為:select * from student where xCode = S123456
如果xCode的數據類型為varchar,那么使用${}就會報錯。
2)${}一般用在order by, limit, group by等場所。
假設我們使用#{} 來指定order by字段,比如
select * from student order by #{xCode},
那么產生的SQL為
select * from student order by ?, 替換值后為
select * from student order by ‘xCode’
Mybatis對xCode加了引號導致排序失敗
2、Spring環境用Mybatis-Spring的接口而不是Mybatis的原生接口
在spring 環境使用mybatis-spring的好處有:
1)我們可以使用Sping的聲明式事務處理模型(@Transactional),而不用手動回歸事務。
2)mybatis-spring會優雅的關閉SqlSession,而不用手動關閉
3)可以將數據庫連接池交給spring管理,當程序停止的時候,spring會合適的關閉連接
3、返回Map<ID, Entity>而不是List便於查找
有時太多的表連接(join)性能太差,我們會將該SQL拆為多個SQL,然后在代碼中組裝起來。比如學生表和班級表,需要查詢的結果為”學號,班級,姓名”,我們可以先查詢“學號,班級ID,姓名”以及“班級ID,班級名次”,我們可以在查詢班級表的時候返回Map<班級ID, 班級>, 然后迭代學生表的結果集,用班級ID到Map<班級ID, 班級>中查找對應的班級信息,然后用班級名稱替換班級ID。
接口聲明為SqlSession.selectMap(String statement, String mapKey)
4、使用Map封裝查詢的結果
有時我們厭倦了為每個查詢寫一個Entity類,這時Map開始發揮它的功效。
對於要返回“學號,班級,姓名”結果的查詢,可以這樣寫Mapper:
1
2
3
4
5
|
<select id="selectStudent">
select s.code as sNo , s.name as sName, c.name as cName
from xStudent s, xClass c
where s.cID = c.ID
</select>
|
如下聲明我們的dao方法:
1
2
3
|
public List<Map<String, Object>> selectStudent(Map<String, Object> parameter) {
return getSqlSession().selectList(getStatement("selectStudent"), parameter);
}
|
如果要將該查詢結果轉為JSON字符串返回,那么我們就可以直接將List<Map<String, Object>轉為JSON,邏輯層不需要任何代碼。
如果返回的結果集需要按select中的字段順序返回,那么將resultType=”Java.util.HashMap” 換為resultType=”java.util.LinkedHashMap”
5、使用Map封裝查詢結果時注意數據的類型映射
對於如下的Mapper
1
2
3
4
|
<select id="selectStudent">
select s.code as sNo , concat(s.firstName, s.lastName) as sName
from xStudent s
</select>
|
Mybatis會傻傻的將sName的數據類型映射為byte[], 因為我們沒有提供entity,mybatis也不知道我們想要什么類型,而sName是計算出來的值,mybatis也沒有辦法從數據庫中獲取字段的值,所以它就將其封裝為byte[],解決辦法很簡單,加一個cast 函數
1
2
3
4
|
<select id="selectStudent">
select s.code as sNo , cast(concat(s.firstName, s.lastName) AS CHAR) as sName
from xStudent s
</select>
|
6、正確的配置Mybatis 的Log
1)一個應用一般會使用很多的jar,各個jar依賴的log 實現不一樣,Mybatis查找Log的順序為(SLF4J,Apache Commons Logging,Log4j 2,Log4j,JDK logging),如果classpath中有slf4j記得添加相應的橋接jar,比如slf4j-log4j。許多web 服務器的classpath 會含有Apache Commons Logging,因此如果要使用Log4j,要么使用SLF4J橋接Log4j,要么在配置中強制指定使用Log4J。
1
2
3
4
5
6
7
|
<configuration>
<settings>
...
<setting name="logImpl" value="LOG4J"/>
...
</settings>
</configuration>
|
7、警惕Mybatis的Foreach的的副作用
對於如下SQL:
假設有如下的mapper:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<select id=”testForeach” parameterType=”map” resultType=”Student”>
Select * from student
<where>
<if test=”ID != null and ID != ‘’ ”>
ID = #{ID}
</if>
<if test=” IDArr != null and IDArr.size()>0”>
And ID IN
<foreach collection="IDArr" open="("
separator="," close=")" item="ID">
${ID}
</foreach>
</if>
</where>
</select>
|
當我們傳入的IDArr時,最后產生的SQL為:
Select * from student where ID = ‘998’ AND ID IN ( ‘123’, ’234’,…..,’998’)
解決辦法:
解決辦法有
1) 將紅色的ID 換成別的名稱,比如“item”。
2) 這兩個if 是對同一個字段判斷,改為choose… when 結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<select id=”testForeach” parameterType=”map” resultType=”Student”>
Select * from student
<where>
<choose>
<when test=”ID != null and ID != ‘’ ”>
ID = #{ID}
</when>
<when test=” IDArr != null and IDArr.size()>0”>
And ID IN
<foreach collection="IDArr" open="("
separator="," close=")" item="ID">
${ID}
</foreach>
</when>
</choose>
</where>
|
8、使用原生的SQL操作數據以提高效率
對於一次插入多條數據,將其組裝成 insert into xxx values (), () ()格式一次插入多行數據往往能極大的提高性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public int execute(String sql) {
int affectedCount = 0;
Connection conn = null;
Statement stm = null;
try {
conn = getSqlSession().getConnection();
stm = conn.createStatement();
stm.execute(sql);
affectedCount = stm.getUpdateCount();
} catch (SQLException e) {
throw new RuntimeException(“execute[" + sql + "] failed”);
} finally {
try{
if(stm != null && !stm.isClosed()) {
stm.close();
}
//conn will be released by mybatis framework
} catch(SQLException e) {
}
}
return affectedCount;
}
|
9、警惕MyBatis封裝數據時性能損耗
對於如下的mapper
1
2
3
4
5
6
7
|
<select id="test" resultType="Student">
select s.code, s.firstName,
s.lastName, s.birthDate, s.sex, s.checkIn, s.phoneNumber,
s.classNo
from student
where ...
</select>
|
在一個批處理程序中循環的調用了該方法250次,每次返回大概1w條記錄,發現這個程序運行的很慢,用jrofiler 查看各個方法耗費的時間,居然80%的時間花在了student的setter上了,在這個過程中大概產生了250w個對象,而mybatis是利用發射封裝Entity,代碼大致如下:
1
2
3
4
5
6
|
Class c= Class.forName("cn.javacoder.testmybatis.Student");
Object o = c.newInstance();
for(each colum) {
Method m = c.getMethod("get" + colum);
m.invoke(o, value);
}
|
解決的辦法是讓返回的行數和返回的字段盡量的少。