Mybatis學習
mybatis http://www.mybatis.org/mybatis-3/zh/index.html
動態(tài)SQL
- MyBatis的加強大在于他的動態(tài)SQL
- 動態(tài) SQL 元素和 JSTL 或基于類似 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多元素需要花時間了解。MyBatis 3 大大精簡了元素種類,現(xiàn)在只需學習原來一半的元素便可。MyBatis 采用功能強大的基于 OGNL 的表達式來淘汰其它大部分元素。
- if(簡單的條件判斷)
- choose(when,otherwize),想到與Java中的switch,與Jstl中的choose類似
- trim(對包含的內(nèi)容加prefix或suffix等)
- where(主要簡化sql語句中的where條件判斷,智能處理and or,不必擔心多余導致語法錯誤)
- set(用于update 更新)
- foreach(sql 中的 in 語句,查詢時特別管用)
if
if就是簡單的條件判斷,利用if語句我們可以實現(xiàn)某些簡單的條件選擇。動態(tài) SQL 通常要做的事情是有條件地包含 where 子句的一部分
<select id="findUser" parameterType="User" resultMap="User">
select id ,username,age from user where 1=1
<if test="username!=null and username!=''">
and username=#{username}
</if>
</select>
choose(when,otherwize)
when元素表示當when中的條件滿足的時候就輸出其中的內(nèi)容,跟JAVA中的switch效果差不多的是按照條件的順序,當when中有條件滿足的時候,
就會跳出choose,即所有的when和otherwise條件中,只有一個會輸出,當所有的我很條件都不滿足的時候就輸出otherwise中的內(nèi)容
<select id="chooseTest" parameterType="User" resultMap="User">
select id,username,age where i=1
<choose>
<when test="username!=null and username!=''">
and username=#{username}
</when>
<otherwise>
and age=#{age}
</otherwise>
</choose>
</select>
trim(prefix,suffix),代替where
trim元素的主要功能是可以在自己包含的內(nèi)容前加上某些前綴,也可以在其后加上某些后綴,與之對應的屬性是prefix和suffix;可以把包含內(nèi)容的首部某些內(nèi)容覆蓋,即忽略,也可以把尾部的某些內(nèi)容覆蓋,對應的屬性是prefixOverrides和suffixOverrides;正因為trim有這樣的功能,所以我們也可以非常簡單的利用trim來代替where元素的功能
<select id="trimTest" parameterType="User" resultMap="User">
select id,username,age form user
<trim prefix="where" prefixOverrides="and | or">
<if test="username!=null and username.length()>0">
username like CONCAT(CONCAT('%',#{usrname}),'%')
</if>
<if test="age!=null and age!=''">
and age=#{age}
</if>
</trim>
</select>
trim代替set標簽
if/trim代替set(判斷參數(shù)) - 將實體類不為空的屬性更新
<update id="updateUser" parameterType="User" >
update user
<trim prefix="set" suffixOverrides=",">
<if test="username!=null">
username=#{username},
</if>
<if test="age!=null">
age=${age}
</if>
</trim>
<where>
id=${id}
</where>
</update>
set
set元素主要是用在更新操作的時候,它的主要功能和where元素其實是差不多的,主要是在set標簽包含的語句前輸出一個set。如果包含的語句是以逗號結(jié)束的話將會把該逗號忽略。如果set包含的內(nèi)容為空的話則會出錯。有了set元素我們就可以動態(tài)的更新那些修改了的字段,而不用每次更新全部字段
<update id="updateUser" parameterType="User">
update user
<set>
<if test="username!=null">
username=#{username}
</if>
</set>
where id=${id}
</update>
foreach
foreach的主要用在構(gòu)建in條件中,它可以在SQL語句中進行迭代一個集合。foreach元素的屬性主要有item,index,collection,open,separator,close
- item 表示集合中的每一個元素進行迭代時的別名
- index 指定一個名字,用于表示迭代過程中每次迭代的位置
- open 表示語句以什么開始
- separator 表示在每次進行迭代之間以什么符號作為分隔符
- close 表示以什么結(jié)束
-
collection 最關(guān)鍵的也是最容易出錯的,該屬性必須指定且在不同情況下屬性值不一樣
- 如果傳入的是單參數(shù)且參數(shù)類型是一個List的時候,collection屬性值為list
- 如果傳入的是單參數(shù)結(jié)參數(shù)類型是一個array數(shù)組的時候,collection的屬性值為array
- 如果傳入的參數(shù)是多個的時候,我們就需要把它們封裝成一個Map了,當然單參數(shù)也可以封裝成map,實際上如果你在傳入?yún)?shù)的時候,在MyBatis里面也是會把它封裝成一個Map的,map的key就是參數(shù)名,所以這個時候collection屬性值就是傳入的List或array對象在自己封裝的map里面的key
- list類型
<select id="foreachList" resultMap="User">
select * from user where id in
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
// public List<User> foreachList(List<Integer> ids);
- array數(shù)組類型
<select id="foreachArray" resultMap="User">
select * from user where id in
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
// public List<User> foreachArray(int[] ids);
- Map 封裝
<select id="foreacherMap" resultType="User">
select * from user where username like CONCAT(CONCAT('%',#{username}),'%') and id in
<foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!--代碼實現(xiàn)-->
@Test
public void foreachMap() {
SqlSession session = Util.getSqlSessionFactory().openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
final List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
Map<String, Object> params = new HashMap<String, Object>();
params.put("ids", ids);
params.put("username", "張三");
List<User> users = userMapper.foreachMap(params);
for (User user : users)
System.out.println(user);
session.close();
}
Mybatis防止SQL注入
Mybatis是啟用SQL預編譯功能的,底層實現(xiàn)原理:是JDBC中的PreparedStatement類在起作用,PreparedStatement是我們很熟悉的Statement的子類,它的對象包含了編譯好的SQL語句。這種“準備好”的方式不僅能提高安全性,而且在多次執(zhí)行同一個SQL時,能夠提高效率。原因是SQL已編譯好,再次執(zhí)行時無需再編譯。
- #{}:相當于JDBC中的PreparedStatement,是經(jīng)過預編譯的,是安全的
- ${}:輸出變量的值,會直接參與SQL編譯,是未經(jīng)過預編譯的,僅僅是取變量的值,存在SQL注入,是非安全的
<select id="getUserById" parameterType="int" resultType="User">
select id,username,age from user
where id=#{id}
</select>
這里,parameterType表示了輸入的參數(shù)類型,resultType表示了輸出的參數(shù)類型。回應上文,如果我們想防止SQL注入,理所當然地要在輸入?yún)?shù)上下功夫.當輸入?yún)?shù)是打印出的sql是:
SELECT id,username,age FROM user WHERE id=?
不管輸入什么參數(shù),打印出的SQL都是這樣的。這是因為MyBatis啟用了預編譯功能,在SQL執(zhí)行前,會先將上面的SQL發(fā)送給數(shù)據(jù)庫進行編譯;執(zhí)行時,直接使用編譯好的SQL,替換占位符“?”就可以了。因為SQL注入只能對編譯過程起作用,所以這樣的方式就很好地避免了SQL注入的問題
以下將 #{} 換成 ${},給參數(shù)"id"賦值2,打印sql如下
SELECT id,username,age FROM user WHERE id=2
差異
// id=3,username=admin or 1=1
select * from user where id=#{id} and username=${username}
<!--執(zhí)行如下-->
select * from where id=? and username=admin or 1=1
//以上執(zhí)行時會執(zhí)行or這樣就存在安全問題
//這里用戶username=id
select * from user order by ${username}
<!--執(zhí)行如下-->
select * from user order by id
//顯然,這樣是無法阻止SQL注入的