Mybatis學習--Mapper.xml對映檔案
簡介
Mapper.xml對映檔案中定義了操作資料庫的sql,每個sql是一個statement,對映檔案是mybatis的核心。
對映檔案中有很多屬性,常用的就是parameterType
(輸入型別)、resultType
(輸出型別)、resultMap()、rparameterMap()。
parameterType(輸入型別)
1、#{}
與${}
#{}
實現的是向prepareStatement中的預處理語句中設定引數值,sql語句中#{}
表示一個佔位符即?。
<!-- 根據id查詢使用者資訊 --> <select id="findUserById" parameterType="int" resultType="user"> select * from user where id = #{id} </select>
使用佔位符#{}
可以有效防止sql注入,在使用時不需要關心引數值的型別,mybatis會自動進行java型別和jdbc型別的轉換。#{}
可以接收簡單型別值或pojo屬性值,如果parameterType
傳輸單個簡單型別值,#{}
括號中可以是value或其它名稱。
${}
和#{}
不同,通過${}
可以將parameterType
傳入的內容拼接在sql中且不進行jdbc型別轉換, ${}
可以接收簡單型別值或pojo屬性值,如果parameterType
傳輸單個簡單型別值,${}
括號中只能是value。使用${}
不能防止sql注入,但是有時用${}
會非常方便,如下的例子:
<!-- 根據名稱模糊查詢使用者資訊 --> <select id="selectUserByName" parameterType="string" resultType="user"> select * from user where username like '%${value}%' </select>
如果本例子使用#{}
則傳入的字串中必須有%號,而%是人為拼接在引數中,顯然有點麻煩,如果採用${}
在sql中拼接為%的方式則在呼叫mapper介面傳遞引數就方便很多。
如果使用佔位符號則必須人為在傳引數中加%
List list = userMapper.selectUserByName("%張三%");
如果使用${}原始符號則不用人為在引數中加%
Listlist = userMapper.selectUserByName(“張三”);
再比如order by排序,如果將列名通過引數傳入sql,根據傳的列名進行排序,應該寫為:
ORDER BY ${columnName},如果使用#{}將無法實現此功能。
2、傳遞簡單型別
傳遞簡單型別只需要注意#{}與${}的使用就可以。
3、傳遞pojo物件
Mybatis使用ognl表示式解析物件欄位的值,如下例子:
<!—傳遞pojo物件綜合查詢使用者資訊 -->
<select id="findUserByUser" parameterType="user" resultType="user">
select * from user where id=#{id} and username like '%${username}%'
</select>
測試程式碼:
Public void testFindUserByUser()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper介面例項
UserMapper userMapper = session.getMapper(UserMapper.class);
//構造查詢條件user物件
User user = new User();
user.setId(1);
user.setUsername("管理員");
//傳遞user物件查詢使用者列表
List<User>list = userMapper.findUserByUser(user);
//關閉session
session.close();
}
如果將username寫錯後,會報以下異常
org.apache.ibatis.exceptions.PersistenceException:
Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘dusername’ in ‘class com.luchao.mybatis.first.po.User’
Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘dusername’ in ‘class com.luchao.mybatis.first.po.User’
可以看出MyBatis是通過反射來講java物件對映到查詢引數中的。
4、傳遞pojo包裝物件
開發中通過pojo傳遞查詢條件 ,查詢條件是綜合的查詢條件,不僅包括使用者查詢條件還包括其它的查詢條件(比如將使用者購買商品資訊也作為查詢條件),這時可以使用包裝物件傳遞輸入引數。
(1)、定義包裝物件
定義包裝物件將查詢條件(pojo)以類組合的方式包裝起來。
public class QueryVo {
private User user;
//自定義使用者擴充套件類
private UserCustom custom;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public UserCustom getCustom() {
return custom;
}
public void setCustom(UserCustom custom) {
this.custom = custom;
}
}
(2)mapper.xml對映檔案
<select id="findUser" parameterType="com.luchao.mybatis.first.po.QueryVo" resultType="com.luchao.mybatis.first.po.User">
select * from user where username like '%${user.username}%' and sex = #{user.sex}
</select>
說明:mybatis底層通過ognl從pojo中獲取屬性值:#{user.username},user即是傳入的包裝物件的屬性。
5、傳遞hashmap
Sql對映檔案定義如下:
<select id="findUserByIdMap" parameterType="hashmap" resultType="com.luchao.mybatis.first.po.User">
select * from user where id = #{id}
</select>
測試程式碼:
Public void testFindUserByHashmap()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper介面例項
UserMapper userMapper = session.getMapper(UserMapper.class);
//構造查詢條件Hashmap物件
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id", 1);
//傳遞Hashmap物件查詢使用者列表
List<User>list = userMapper.findUserByHashmap(map);
//關閉session
session.close();
}
傳遞的map中的key和sql中解析的key不一致。測試結果沒有報錯,只是通過key獲取值為空。這種用法一般用在POJO與資料庫欄位不一致的時候。
parameterMap和resultMap
resultType可以指定pojo將查詢結果對映為pojo,但需要pojo的屬性名和sql查詢的列名一致方可對映成功。如果sql查詢欄位名和pojo的屬性名不一致,可以通過resultMap將欄位名和屬性名作一個對應關係 ,resultMap實質上還需要將查詢結果對映到pojo物件中。resultMap可以實現將查詢結果對映為複雜型別的pojo,比如在查詢結果對映物件中包括pojo和list實現一對一查詢和一對多查詢。
下面是在資料庫列於POJO不一致的時候,將輸入引數對映到資料庫列的一種方式
<resultMap type="Book.dao.Book" id="BookResultMap">
<id column="id" property="id"/>
<result column="name" property="bookName"/>
<result column="price" property="bookPrice"/>
</resultMap>
<!-- resultMap:resultMap的id ,bookName:resultMap的property,即實體類中的屬性 -->
<parameterMap type="Book.dao.Book" id="BookParameterMap">
<parameter property="bookName" resultMap="BookResultMap" />
<parameter property="bookPrice" resultMap="BookResultMap" />
</parameterMap>
<!-- 儲存一個Book -->
<insert id="saveBook" parameterMap="BookParameterMap">
insert into BOOK_MANAGE
(ID,NAME,PRICE)
values
(Bookmanage_Seq.Nextval,#{bookName},#{bookPrice})
</insert>
<!-- 根據ID修改Book -->
<update id="updatePersnById" parameterMap="BookParameterMap">
update BOOK_MANAGE
set
NAME=#{bookName},
PRICE=#{bookPrice}
WHERE id=#{id}
</update>
當查詢的結果與POJO名字不一致的時候,用resultMap來實現對映。
<resultMap type="user" id="userMap">
<id column="id_" property="id" />
<result column="username_" property="username" />
</resultMap>
<select id="findUserMapById" parameterType="java.lang.Integer" resultMap="userMap" >
select id id_,username username_ from user where id = #{id}
</select>
:此屬性表示查詢結果集的唯一標識,非常重要。如果是多個欄位為複合唯一約束則定義多個。
Property:表示person類的屬性。
Column:表示sql查詢出來的欄位名。
Column和property放在一塊兒表示將sql查詢出來的欄位對映到指定的pojo類屬性上。
<result />
:普通結果,即pojo的屬性。
使用resultType進行輸出對映,只有查詢出來的列名和pojo中的屬性名一致,該列才可以對映成功。如果查詢出來的列名和pojo的屬性名不一致,通過定義一個resultMap對列名和pojo屬性名之間作一個對映關係。
resultType(輸出型別)
1、輸出簡單型別
對映檔案:
<select id="findUserCount" parameterType="user" resultType="int">
select count(1) from user
</select>
輸出簡單型別必須查詢出來的結果集有一條記錄,最終將第一個欄位的值轉換為輸出型別。使用session的selectOne可查詢單條記錄。
2、輸出pojo物件
對映檔案
<!-- 根據id查詢使用者資訊 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
3、輸出pojo列表
對映檔案:
<!-- 根據名稱模糊查詢使用者資訊 -->
<select id="findUserByUsername" parameterType="string" resultType="user">
select * from user where username like '%${value}%'
</select>
注意:MyBatis會根據Mapper介面方法的返回型別來選擇呼叫selectOne還是selectList方法,如果是List這呼叫selectList方法,如果是POJO則呼叫selectOne方法。
4、輸出hashmap
輸出pojo物件可以改用hashmap輸出型別,將輸出的欄位名稱作為map的key,value為欄位值。
resultType總結:
輸出pojo物件和輸出pojo列表在sql中定義的resultType是一樣的。返回單個pojo物件要保證sql查詢出來的結果集為單條,內部使用session.selectOne方法呼叫,mapper介面使用pojo物件作為方法返回值。返回pojo列表表示查詢出來的結果集可能為多條,內部使用session.selectList方法,mapper介面使用List物件作為方法返回值。
動態SQl
mybatis核心 對sql語句進行靈活操作,通過表示式進行判斷,對sql進行靈活拼接、組裝。對查詢條件進行判斷,如果輸入引數不為空才進行查詢條件拼接。
1、if
<!-- 傳遞pojo綜合查詢使用者資訊 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
2、Where
上面的配置也可以按如下來寫:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
可以自動處理第一個and。
3、foreach
向sql傳遞陣列或List,mybatis使用foreach解析,如下:
如果我們需要傳入多個ID來查詢多個使用者的資訊,這也就可以使用foreach。我們先考慮下如果只寫sql語句是如下:
SELECT * FROM USERS WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%張%' id IN (10,89,16)
index:為陣列的下標。
item:為陣列每個元素的名稱,名稱隨意定義
open:迴圈開始
close:迴圈結束
separator:中間分隔輸出
通過POJO傳入List,對映檔案如下:
<if test="ids!=null and ids.size>0">
<foreach collection="ids" open=" and id in(" close=")" item="id" separator="," > #{id}
</foreach>
</if>
或者:
<if test="ids!=null and ids.size>0">
<foreach collection="ids" open=" and (" close=")" item="id" separator="," >
id = #{id}
</foreach>
</if>
傳遞單個List
傳遞List型別在編寫mapper.xml沒有區別,唯一不同的是隻有一個List引數時它的引數名為list。
配置檔案如下:
<select id="selectUserByList" parameterType="java.util.List" resultType="user">
select * from user
<where>
<!-- 傳遞List,List中是pojo -->
<if test="list!=null">
<foreach collection="list" item="item" open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
傳遞單個數組(陣列中是POJO)
<!-- 傳遞陣列綜合查詢使用者資訊 -->
<select id="selectUserByArray" parameterType="Object[]" resultType="user">
select * from user
<where>
<!-- 傳遞陣列 -->
<if test="array!=null">
<foreach collection="array" index="index" item="item" open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
sql只接收一個數組引數,這時sql解析引數的名稱mybatis固定為array,如果陣列是通過一個pojo傳遞到sql則引數的名稱為pojo中的屬性名。
傳遞單個數組(陣列中是簡單型別)
配置檔案如下:
<!-- 傳遞陣列綜合查詢使用者資訊 -->
<select id="selectUserByArray" parameterType="Object[]" resultType="user">
select * from user
<where>
<!-- 傳遞陣列 -->
<if test="array!=null">
<foreach collection="array"index="index"item="item"open="and id in("separator=","close=")">
#{item}
</foreach>
</if>
</where>
</select>
如果陣列中是簡單型別則寫為#{item},不用再通過ognl獲取物件屬性值了。
Sql片段
Sql中可將重複的sql提取出來,使用時用include引用即可,最終達到sql重用的目的,如下:
對映檔案如下:
<!-- 傳遞pojo綜合查詢使用者資訊 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
如果有多個statement都使用相同的查詢條件,那麼就可以把查詢條件抽取出來作為單獨的Sql片段。
Sql片段配置:
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</sql>
使用include引用:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<include refid="query_user_where"/>
</where>
</select>
注意:如果引用其它mapper.xml的sql片段,則在引用時需要加上namespace,如下:<include refid="namespace.sql片段”/>
Mapper配置檔案中常用的基本屬性就是這些,如果還有其他的特殊需求可以根據需要來進行修改配置。另外,在我們的設計中,如果已經定義好了基本的POJO在引用的時候可以在定義一個檢視查詢層的POJO在其中封裝基本的POJO和自定義的POJO(繼承基本的POJO),這樣就可以較容易實現擴充套件。當資料庫需求有變化的時候可以不修改基本POJO,而修改自定義的POJO,這樣就可以實現較好的擴充套件,而不影響其他模組。如果前端需求有變動,可以通過修改前端的POJO來實現較小的改動。如下實現:
基本的POJO型別:
public class User {
private int id;
private String username;// 使用者姓名
private String sex;// 性別
private Date birthday;// 生日
private String address;// 地址
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return this.id+"-"+this.username+"-"+this.sex+"-"+this.address+"-"+this.birthday.toString();
}
}
自定義的POJO繼承基本的POJO:
public class UserCustom extends User{
}
如果我們的資料庫有變動,我們可以在UserCustom新增屬性,只滿足當前修改。
前端POJO實現:
public class QueryVo {
private User user;
//自定義使用者擴充套件類
private UserCustom custom;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public UserCustom getCustom() {
return custom;
}
public void setCustom(UserCustom custom) {
this.custom = custom;
}
}
可以滿足基本的需求,如果我們在查詢中需要加入其他查詢條件,如:商品、訂單等,只需要修改QueryVo,這樣就可以實現較好的可擴充套件性。