Mybatis學習--Mapper.xml映射文件


  • 簡介

  Mapper.xml映射文件中定義了操作數據庫的sql,每個sql是一個statement,映射文件是mybatis的核心。

  映射文件中有很多屬性,常用的就是parameterType(輸入類型)、resultType(輸出類型)、resultMap()、rparameterMap()。

  • parameterType(輸入類型)

  1、#{}與${}

  #{}實現的是向prepareStatement中的預處理語句中設置參數值,sql語句中#{}表示一個占位符即?

 

1     <!-- 根據id查詢用戶信息 -->
2     <select id="findUserById" parameterType="int" resultType="user">
3         select * from user where id = #{id}
4     </select>        

 

  使用占位符#{}可以有效防止sql注入,在使用時不需要關心參數值的類型,mybatis會自動進行java類型和jdbc類型的轉換。#{}可以接收簡單類型值或pojo屬性值,如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其它名稱。

  ${}#{}不同,通過${}可以將parameterType 傳入的內容拼接在sql中且不進行jdbc類型轉換, ${}可以接收簡單類型值或pojo屬性值,如果parameterType傳輸單個簡單類型值,${}括號中只能是value。使用${}不能防止sql注入,但是有時用${}會非常方便,如下的例子:

  

1   <!-- 根據名稱模糊查詢用戶信息 -->
2     <select id="selectUserByName" parameterType="string" resultType="user">
3        select * from user where username like '%${value}%'
4     </select>

 

  如果本例子使用#{}則傳入的字符串中必須有%號,而%是人為拼接在參數中,顯然有點麻煩,如果采用${}sql中拼接為%的方式則在調用mapper接口傳遞參數就方便很多。

  //如果使用占位符號則必須人為在傳參數中加%

 

  List<User> list = userMapper.selectUserByName("%張三%");

  //如果使用${}原始符號則不用人為在參數中加%

 

  List<User>list = userMapper.selectUserByName("張三");

  再比如order by排序,如果將列名通過參數傳入sql,根據傳的列名進行排序,應該寫為:

  ORDER BY ${columnName},如果使用#{}將無法實現此功能。

  2、傳遞簡單類型

  傳遞簡單類型只需要注意#{}與${}的使用就可以。

  3、傳遞pojo對象

  Mybatis使用ognl表達式解析對象字段的值,如下例子:

 

1   <!—傳遞pojo對象綜合查詢用戶信息 -->
2     <select id="findUserByUser" parameterType="user" resultType="user">
3        select * from user where id=#{id} and username like '%${username}%'
4     </select>

 

  測試代碼:

 1 Public void testFindUserByUser()throws Exception{
 2         //獲取session
 3         SqlSession session = sqlSessionFactory.openSession();
 4         //獲限mapper接口實例
 5         UserMapper userMapper = session.getMapper(UserMapper.class);
 6         //構造查詢條件user對象
 7         User user = new User();
 8         user.setId(1);
 9         user.setUsername("管理員");
10         //傳遞user對象查詢用戶列表
11         List<User>list = userMapper.findUserByUser(user);
12         //關閉session
13         session.close();
14 }

  如果將username寫錯后,會報以下異常

1 org.apache.ibatis.exceptions.PersistenceException: 
2 ### 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'
3 ### 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)以類組合的方式包裝起來。

 

 1 public class QueryVo {
 2     private User user;
 3     //自定義用戶擴展類
 4     private UserCustom custom;
 5     public User getUser() {
 6         return user;
 7     }
 8     public void setUser(User user) {
 9         this.user = user;
10     }
11     public UserCustom getCustom() {
12         return custom;
13     }
14     public void setCustom(UserCustom custom) {
15         this.custom = custom;
16     }
17 }

 

  (2)、 mapper.xml映射文件

 

1 <select id="findUser" parameterType="com.luchao.mybatis.first.po.QueryVo" resultType="com.luchao.mybatis.first.po.User">
2         select * from user where username like '%${user.username}%' and sex = #{user.sex}
3 </select>

 

  說明:mybatis底層通過ognlpojo中獲取屬性值:#{user.username}user即是傳入的包裝對象的屬性。

  5、傳遞hashmap

  Sql映射文件定義如下:

 

1 <select id="findUserByIdMap" parameterType="hashmap" resultType="com.luchao.mybatis.first.po.User">
2         select * from user where id = #{id}
3 </select>

 

  測試代碼:

 1 Public void testFindUserByHashmap()throws Exception{
 2         //獲取session
 3         SqlSession session = sqlSessionFactory.openSession();
 4         //獲限mapper接口實例
 5         UserMapper userMapper = session.getMapper(UserMapper.class);
 6         //構造查詢條件Hashmap對象
 7         HashMap<String, Object> map = new HashMap<String, Object>();
 8         map.put("id", 1);        
 9         //傳遞Hashmap對象查詢用戶列表
10         List<User>list = userMapper.findUserByHashmap(map);
11         //關閉session
12         session.close();
13 }

  傳遞的map中的keysql中解析的key不一致。測試結果沒有報錯,只是通過key獲取值為空。這種用法一般用在POJO與數據庫字段不一致的時候。

  • parameterMap和resultMap

  resultType可以指定pojo將查詢結果映射為pojo,但需要pojo的屬性名和sql查詢的列名一致方可映射成功。如果sql查詢字段名和pojo的屬性名不一致,可以通過resultMap將字段名和屬性名作一個對應關系 ,resultMap實質上還需要將查詢結果映射到pojo對象中。resultMap可以實現將查詢結果映射為復雜類型的pojo,比如在查詢結果映射對象中包括pojolist實現一對一查詢和一對多查詢。

 

  下面是在數據庫列於POJO不一致的時候,將輸入參數映射到數據庫列的一種方式 

 1 <resultMap type="Book.dao.Book" id="BookResultMap">
 2   <id column="id" property="id"/>
 3   <result column="name" property="bookName"/>
 4   <result column="price" property="bookPrice"/>
 5  </resultMap>
 6  
 7  <!-- resultMap:resultMap的id ,bookName:resultMap的property,即實體類中的屬性 -->
 8  <parameterMap type="Book.dao.Book" id="BookParameterMap">
 9   <parameter property="bookName" resultMap="BookResultMap" />  
10   <parameter property="bookPrice" resultMap="BookResultMap" />  
11  </parameterMap>
12 <!-- 保存一個Book -->
13  <insert id="saveBook" parameterMap="BookParameterMap">
14   insert into BOOK_MANAGE
15    (ID,NAME,PRICE)
16     values
17    (Bookmanage_Seq.Nextval,#{bookName},#{bookPrice})
18  </insert>
19  
20  <!-- 根據ID修改Book -->
21  <update id="updatePersnById" parameterMap="BookParameterMap">
22   update BOOK_MANAGE 
23    set 
24     NAME=#{bookName},
25     PRICE=#{bookPrice}
26    WHERE id=#{id}
27  </update>

 

  當查詢的結果與POJO名字不一致的時候,用resultMap來實現映射。

1 <resultMap type="user" id="userMap">
2         <id column="id_" property="id" />
3         <result column="username_" property="username" />
4 </resultMap>
5 <select id="findUserMapById" parameterType="java.lang.Integer" resultMap="userMap" >
6         select id id_,username username_ from user where id = #{id}
7 </select>

  <id />:此屬性表示查詢結果集的唯一標識,非常重要。如果是多個字段為復合唯一約束則定義多個<id />

  Property:表示person類的屬性。

  Column:表示sql查詢出來的字段名。

  Columnproperty放在一塊兒表示將sql查詢出來的字段映射到指定的pojo類屬性上。

    <result />:普通結果,即pojo的屬性。

 

  使用resultType進行輸出映射,只有查詢出來的列名和pojo中的屬性名一致,該列才可以映射成功。如果查詢出來的列名和pojo的屬性名不一致,通過定義一個resultMap對列名和pojo屬性名之間作一個映射關系。

  • resultType(輸出類型)

  1、輸出簡單類型

  映射文件:

 

1 <select id="findUserCount" parameterType="user" resultType="int">
2        select count(1) from user
3 </select>

 

  輸出簡單類型必須查詢出來的結果集有一條記錄,最終將第一個字段的值轉換為輸出類型。使用sessionselectOne可查詢單條記錄。

  2、輸出pojo對象

  映射文件

 

1         <!-- 根據id查詢用戶信息 -->
2     <select id="findUserById" parameterType="int" resultType="user">
3         select * from user where id = #{id}
4     </select>    

 

  3、輸出pojo列表

  映射文件:

 

1 <!-- 根據名稱模糊查詢用戶信息 -->
2     <select id="findUserByUsername" parameterType="string" resultType="user">
3        select * from user where username like '%${value}%'
4     </select>

 

  注意:MyBatis會根據Mapper接口方法的返回類型來選擇調用selectOne還是selectList方法,如果是List這調用selectList方法,如果是POJO則調用selectOne方法。

  4、輸出hashmap

 

  輸出pojo對象可以改用hashmap輸出類型,將輸出的字段名稱作為mapkeyvalue為字段值。

  resultType總結:

 

  輸出pojo對象和輸出pojo列表在sql中定義的resultType是一樣的。返回單個pojo對象要保證sql查詢出來的結果集為單條,內部使用session.selectOne方法調用,mapper接口使用pojo對象作為方法返回值。返回pojo列表表示查詢出來的結果集可能為多條,內部使用session.selectList方法,mapper接口使用List<pojo>對象作為方法返回值。

  • 動態SQl

 

  mybatis核心 對sql語句進行靈活操作,通過表達式進行判斷,對sql進行靈活拼接、組裝。對查詢條件進行判斷,如果輸入參數不為空才進行查詢條件拼接。

  1、if

 1 <!-- 傳遞pojo綜合查詢用戶信息 -->
 2     <select id="findUserList" parameterType="user" resultType="user">
 3         select * from user 
 4         where 1=1 
 5         <if test="id!=null and id!=''">
 6         and id=#{id}
 7         </if>
 8         <if test="username!=null and username!=''">
 9         and username like '%${username}%'
10         </if>
11 </select>

  2、Where

  上面的配置也可以按如下來寫:

 1 <select id="findUserList" parameterType="user" resultType="user">
 2         select * from user 
 3         <where>
 4         <if test="id!=null and id!=''">
 5         and id=#{id}
 6         </if>
 7         <if test="username!=null and username!=''">
 8         and username like '%${username}%'
 9         </if>
10         </where>
11 </select>

  <where />可以自動處理第一個and

  3、foreach

  sql傳遞數組或Listmybatis使用foreach解析,如下:

  如果我們需要傳入多個ID來查詢多個用戶的信息,這也就可以使用foreach。我們先考慮下如果只寫sql語句是如下:

 

1 SELECT * FROM USERS WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
2 SELECT * FROM USERS WHERE username LIKE '%張%'  id IN (10,89,16)

 

  index:為數組的下標。

  item:為數組每個元素的名稱,名稱隨意定義

  open:循環開始

  close:循環結束

  separator:中間分隔輸出

  通過POJO傳入List,映射文件如下:

1 <if test="ids!=null and ids.size>0">
2             <foreach collection="ids" open=" and id in(" close=")" item="id" separator="," >
3                 #{id}
4             </foreach>
5 </if>

  或者:

1 <if test="ids!=null and ids.size>0">
2             <foreach collection="ids" open=" and (" close=")" item="id" separator="," >
3                  id = #{id}
4             </foreach>
5 </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中的屬性名。

  傳遞單個數組(數組中是簡單類型)

  配置文件如下:

 1 <!-- 傳遞數組綜合查詢用戶信息 -->
 2 <select id="selectUserByArray" parameterType="Object[]" resultType="user">
 3         select * from user 
 4         <where>
 5         <!-- 傳遞數組 -->
 6         <if test="array!=null">
 7         <foreach collection="array"index="index"item="item"open="and id in("separator=","close=")">
 8             #{item} 
 9         </foreach>
10         </if>
11         </where>
12 </select>

  如果數組中是簡單類型則寫為#{item},不用再通過ognl獲取對象屬性值了。

 

    Sql片段

  Sql中可將重復的sql提取出來,使用時用include引用即可,最終達到sql重用的目的,如下:

  映射文件如下:

 

 1 <!-- 傳遞pojo綜合查詢用戶信息 -->
 2     <select id="findUserList" parameterType="user" resultType="user">
 3         select * from user 
 4         <where>
 5         <if test="id!=null and id!=''">
 6         and id=#{id}
 7         </if>
 8         <if test="username!=null and username!=''">
 9         and username like '%${username}%'
10         </if>
11         </where>
12     </select>

 

  如果有多個statement都使用相同的查詢條件,那么就可以把查詢條件抽取出來作為單獨的Sql片段。

  Sql片段配置:

1 <sql id="query_user_where">
2     <if test="id!=null and id!=''">
3         and id=#{id}
4     </if>
5     <if test="username!=null and username!=''">
6         and username like '%${username}%'
7     </if>
8 </sql>

  使用include引用:

 

1 <select id="findUserList" parameterType="user" resultType="user">
2         select * from user 
3         <where>
4         <include refid="query_user_where"/>
5         </where>
6 </select>

 

  注意:如果引用其它mapper.xmlsql片段,則在引用時需要加上namespace,如下:<include refid="namespace.sql片段”/>

  Mapper配置文件中常用的基本屬性就是這些,如果還有其他的特殊需求可以根據需要來進行修改配置。另外,在我們的設計中,如果已經定義好了基本的POJO在引用的時候可以在定義一個視圖查詢層的POJO在其中封裝基本的POJO和自定義的POJO(繼承基本的POJO),這樣就可以較容易實現擴展。當數據庫需求有變化的時候可以不修改基本POJO,而修改自定義的POJO,這樣就可以實現較好的擴展,而不影響其他模塊。如果前端需求有變動,可以通過修改前端的POJO來實現較小的改動。如下實現:

  基本的POJO類型:

 1 public class User {
 2     private int id;
 3     private String username;// 用戶姓名
 4     private String sex;// 性別
 5     private Date birthday;// 生日
 6     private String address;// 地址
 7     public int getId() {
 8         return id;
 9     }
10     public void setId(int id) {
11         this.id = id;
12     }
13     public String getUsername() {
14         return username;
15     }
16     public void setUsername(String username) {
17         this.username = username;
18     }
19     public String getSex() {
20         return sex;
21     }
22     public void setSex(String sex) {
23         this.sex = sex;
24     }
25     public Date getBirthday() {
26         return birthday;
27     }
28     public void setBirthday(Date birthday) {
29         this.birthday = birthday;
30     }
31     public String getAddress() {
32         return address;
33     }
34     public void setAddress(String address) {
35         this.address = address;
36     }
37     @Override
38     public String toString() {
39         // TODO Auto-generated method stub
40         return this.id+"-"+this.username+"-"+this.sex+"-"+this.address+"-"+this.birthday.toString();
41     }
42     
43 }

  自定義的POJO繼承基本的POJO:

1 public class UserCustom extends User{
2 
3 }

  如果我們的數據庫有變動,我們可以在UserCustom添加屬性,只滿足當前修改。

  前端POJO實現:

 1 public class QueryVo {
 2     private User user;
 3     //自定義用戶擴展類
 4     private UserCustom custom;
 5     public User getUser() {
 6         return user;
 7     }
 8     public void setUser(User user) {
 9         this.user = user;
10     }
11     public UserCustom getCustom() {
12         return custom;
13     }
14     public void setCustom(UserCustom custom) {
15         this.custom = custom;
16     }
17 }

  可以滿足基本的需求,如果我們在查詢中需要加入其他查詢條件,如:商品、訂單等,只需要修改QueryVo,這樣就可以實現較好的可擴展性。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM