1. 程式人生 > >Mapper.xml對映檔案---Mybatis學習筆記(八)

Mapper.xml對映檔案---Mybatis學習筆記(八)

Mapper.xml對映檔案中定義了操作資料庫的sql,每個sql是一個statement,對映檔案是mybatis的核心。

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<User> list = userMapper.selectUserByName("%管理員%"
);
//如果使用${}原始符號則不用人為在引數中加%
List<User>list = userMapper.selectUserByName("管理員");

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

ORDER BY ${columnName}

如果使用#{}將無法實現此功能。

2.傳遞簡單型別

<select id="findUserById" parameterType="int" resultType="user">
    select * from user where id=#{id}
</select>

3.傳遞pojo物件

Mybatis使用ognl表示式解析物件欄位的值,如下例子:

<!—傳遞pojo物件綜合查詢使用者資訊 -->
    <select id="findUserByUser" parameterType="user" resultType="user">
       select * from user where id=#{id} and username like '%${username}%'
    </select>

上邊sql語句中的id和username是user物件中的欄位名稱。
注意:parameterType中的user是事先在sqlMapConfig.xml檔案中配置了別名的,如下:

    <typeAliases>
        <!-- 針對單個別名的定義 -->
        <typeAlias alias="user" type="com.huihui.pojo.User"/>
    </typeAliases>

4.傳遞pojo包裝物件

需求:
開發中通過pojo傳遞查詢條件 ,查詢條件是綜合的查詢條件,不僅包括使用者查詢條件還包括其它的查詢條件(比如將使用者購買商品資訊也作為查詢條件),這時可以使用包裝物件傳遞輸入引數。

1.自定義包裝物件:
定義包裝物件將查詢條件(pojo)以類組合的方式包裝起來。

package com.huihui.pojo;

/**
 * 使用者的包裝型別
 * @author 62347
 *
 */
public class UserQueryVo {

    //使用者查詢條件
    private User user;
    //用到的其它類條件
    //......

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }


}

2.對映檔案:

<select id="findUserList" parameterType="com.huihui.pojo.UserQueryVo" resultType="user">
    select * from user where username=#{user.username} and sex=#{user.sex}
</select>

說明:mybatis底層通過ognl從pojo中獲取屬性值:#{user.username},user即是傳入的包裝物件的屬性。com.huihui.pojo.UserQueryVo是定義的包裝物件型別。

3.Mapper介面

public interface UserMapper {
    public List<User> findUserList(UserQueryVo queryVo) throws Exception;

}

5.傳遞hashmap

對映檔案:

<!-- 傳遞hashmap綜合查詢使用者資訊 -->
    <select id="findUserByHashmap" parameterType="hashmap" resultType="user">
       select * from user where id=#{id} and username like '%${username}%'
    </select>

注意:
1.sql語句中的id和username是hashmap的key。
2.parameterType中的hashmap是別名

Mapper介面:

public interface UserMapper {
    public User findUserByHashMap(Map map) throws Exception;
}

測試類:

    @Test
    public void testFindUserByHashMap() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("id", 26);
        map.put("username", "王五");
        User user = userMapper.findUserByHashMap(map);
        System.out.println(user);
    }

1.resultType(輸出型別)

使用resultType進行輸出對映,只有查詢出來的列名和pojo中的屬性名一致,該列才可以對映成功。
如果查詢出來的列名和pojo中的屬性名全部不一致,那麼就不會建立pojo物件。
只要查詢出來的列名和pojo中的屬性有一個一致,就會建立pojo物件。

輸出簡單型別:

對映檔案:

<!-- 獲取使用者列表總數 -->
<select id="findUserCount" resultType="int">
    select count(*) from user
</select>

Mapper介面:

public int findUserCount() throws Exception;

測試類:

Public void testFindUserCount() throws Exception{
        //獲取session
        SqlSession session = sqlSessionFactory.openSession();
        //獲取mapper介面例項
        UserMapper userMapper = session.getMapper(UserMapper.class);

        int count = userMapper.findUserCount();

        //關閉session
        session.close();
    }

總結:
輸出簡單型別必須查詢出來的結果集有一條記錄,最終將第一個欄位的值轉換為輸出型別。
使用session的selectOne可查詢單條記錄。

輸出pojo物件:

對映檔案:

    <!-- 根據id查詢使用者資訊 -->
    <select id="findUserById" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>

mapper介面:

public User findUserById(int id) throws Exception;

測試:

Public void testFindUserById() throws Exception {
        //獲取session
        SqlSession session = sqlSessionFactory.openSession();
        //獲限mapper介面例項
        UserMapper userMapper = session.getMapper(UserMapper.class);
        //通過mapper介面呼叫statement
        User user = userMapper.findUserById(1);
        System.out.println(user);
        //關閉session
        session.close();
    }

這裡使用session呼叫selectOne查詢單條記錄。

輸出pojo列表:

對映檔案:

<!-- 根據名稱模糊查詢使用者資訊 -->
    <select id="findUserByUsername" parameterType="string" resultType="user">
       select * from user where username like '%${value}%'
    </select>

mapper介面:

public List<User> findUserByUsername(String username) throws Exception;

測試:

Public void testFindUserByUsername()throws Exception{
        //獲取session
        SqlSession session = sqlSessionFactory.openSession();
        //獲限mapper介面例項
        UserMapper userMapper = session.getMapper(UserMapper.class);
        //如果使用佔位符號則必須人為在傳引數中加%
        //List<User> list = userMapper.selectUserByName("%管理員%");
        //如果使用${}原始符號則不用人為在引數中加%
        List<User> list = userMapper.findUserByUsername("管理員");
        //關閉session
        session.close();
    }

這裡使用session的selectList方法獲取pojo列表。

輸出hashmap:

輸出pojo物件可以改用hashmap輸出型別,將輸出的欄位名稱作為map的key,value為欄位值。

resultType總結:

輸出pojo物件和輸出pojo列表在sql中定義的resultType是一樣的。

返回單個pojo物件要保證sql查詢出來的結果集為單條,內部使用session.selectOne方法呼叫,mapper介面使用pojo物件作為方法返回值。

返回pojo列表表示查詢出來的結果集可能為多條,內部使用session.selectList方法,mapper介面使用List物件作為方法返回值。

2.resultMap(輸出型別)

resultType可以指定pojo將查詢結果對映為pojo,但需要pojo的屬性名和sql查詢的列名一致方可對映成功。
如果sql查詢欄位名和pojo的屬性名不一致,可以通過resultMap將欄位名和屬性名作一個對應關係 ,resultMap實質上還需要將查詢結果對映到pojo物件中。
resultMap可以實現將查詢結果對映為複雜型別的pojo,比如在查詢結果對映物件中包括pojo和list實現一對一查詢和一對多查詢。
1.對映檔案:

<!--將select id id_,username username_ from user查詢結果與User類中的屬性做一個對映關係 -->
<!-- 
type:resultMap最終對映的java物件型別 
id:對resultMap的唯一標識
-->
<resultMap type="user" id="userResultMap">
<!-- 
id表示對查詢結果集中唯一列的標識
column:查詢出來的列名
property:上面type所指定的pojo中的屬性名
最終resultMap對column和property做一個對映關係(對應關係)
 -->
    <id column="id_" property="id"/>
<!-- 
result表示對查詢結果集中普通列的標識
column:查詢出來的列名
property:上面type所指定的pojo中的屬性名
最終resultMap對column和property做一個對映關係(對應關係)
 -->
    <result column="username_" property="username"/>
</resultMap>

<!-- 
resultMap中填寫上面的<resultMap>標籤的id 
注意:如果這個resultMap在其它的<mapper>標籤中,前邊需要加<mapper>標籤的namespace,eg:<mapper>標籤的namespace+<resultMap>標籤的id
-->
<select id="findUserInfo" parameterType="int" resultMap="userResultMap">
    select id id_,username username_ from user where id=#{value}
</select>

說明:
<id />:此屬性表示查詢結果集的唯一標識,非常重要。如果是多個欄位為複合唯一約束則定義多個<id />
Property:表示person類的屬性。
Column:表示sql查詢出來的欄位名。
Column和property放在一塊兒表示將sql查詢出來的欄位對映到指定的pojo類屬性上。

:普通結果,即pojo的屬性。

2.Mapper介面

public User findUserInfo(int id) throws Exception;

3.測試:

    @Test
    public void testFindUserInfo() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserInfo(24);
        System.out.println(user);
        sqlSession.close();
    }

動態sql

通過mybatis提供的各種標籤方法實現動態拼接sql。

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>

注意要做不等於空字串校驗。

where

上邊的sql也可以改為:

<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。

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>

將where條件抽取出來:

<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片段”/>

經驗:

  1. 一個sql片段中最好是基於一張表,這樣的話sql片段的可重用性才高。
  2. 在sql片段中不要包括where

foreach

向sql傳遞陣列或List,mybatis使用foreach解析,如下:
1. 通過pojo傳遞list
需求:
傳入多個id查詢使用者資訊,用下邊兩個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)

在pojo中定義list屬性ids儲存多個使用者id,並新增getter/setter方法

public class UserQueryIds {

    //傳遞多個使用者id
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

}

對映檔案(mapper.xml):

<select id="findUserByIds" parameterType="com.huihui.pojo.UserQueryIds" resultType="user">
    select * from user
    <where>
        <if test="ids!=null and ids.size>0">
            <foreach collection="ids" item="id" open="and id in (" close=")" separator=",">
                #{id}
            </foreach>
        </if>
    </where>
</select>

說明:
collection:指定輸入物件中集合屬性名(也就是上面pojo中定義的屬性名)
item:定義每次遍歷後生成的物件的物件名
open:開始遍歷時要拼接的字串
close:結束遍歷時要拼接的字串
separator:定義遍歷後產生的每個物件之間的字串
<foreach></foreach>之間的內容:表示每次遍歷需要拼接的字串

mapper介面:

    public List<User> findUserByIds(UserQueryIds ids) throws Exception;

測試:

    @Test
    public void testFindUserByIds() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        UserQueryIds ids = new UserQueryIds();
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(10);
        list.add(28);
        ids.setIds(list);
        List<User> users = userMapper.findUserByIds(ids);
        System.out.println(users.size());

        sqlSession.close();
    }

2.傳遞單個List
傳遞List型別在編寫mapper.xml沒有區別,唯一不同的是隻有一個List引數時它的引數名為list。
對映檔案:

<select id="findUserBymIds" parameterType="java.util.List" resultType="user">
    select * from user
    <where>
        <if test="list!=null">
            <foreach collection="list" item="id" open="and id in(" close=")" separator="," >
                #{id}
            </foreach>
        </if>
    </where>
</select>

mapper介面:

    public List<User> findUserBymIds(List<Integer> list) throws Exception;

測試:

    @Test
    public void testFindUserBymIds() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(10);
        list.add(28);
        List<User> users = userMapper.findUserBymIds(list);
        System.out.println(users.size());
        sqlSession.close();
    }

3.傳遞單個數組(陣列中時pojo)
對映檔案:

<select id="findUserByArray" parameterType="Object[]" resultType="user">
    select * from user
    <where>
        <if test="array!=null">
            <foreach collection="array" index="index" item="item" open="and id in(" close=")" separator=",">
                #{item.id}
            </foreach>
        </if>
    </where>
</select>

說明:
sql只接收一個數組引數,這時sql解析引數的名稱mybatis固定為array,如果陣列是通過一個pojo傳遞到sql則引數的名稱為pojo中的屬性名。
index:為陣列的下標。
item:為遍歷後生成的陣列每個元素的名稱,名稱隨意定義
open:迴圈開始需要拼接的字串
close:迴圈結束需要拼接的字串
separator:定義遍歷後產生的每個物件之間的字串

mapper介面:

public List<User> findUserByArray(Object[] objs) throws Exception;

測試:

    @Test
    public void testFindUserByArray() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user1 = new User();
        user1.setId(1);
        User user2 = new User();
        user2.setId(10);
        User user3 = new User();
        user3.setId(28);

        User[] users = {user1,user2,user3};
        List<User> list = userMapper.findUserByArray(users);
        System.out.println(list.size());
        sqlSession.close();
    }

4.傳遞單個數組(陣列中是字串型別)
對映檔案:

<select id="findUserByArray1" parameterType="Object[]" resultType="user">
    select * from user
    <where>
        <if test="array!=null">
            <foreach collection="array" index="index" item="item" open="and id in(" close=")" separator=",">
                #{item}
            </foreach>
        </if>
    </where>
</select>

mapper介面:

public List<User> findUserByArray1(Object[] objs) throws Exception;

測試:

    @Test
    public void testFindUserByArray1() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        String[] ids = {"1","10","28"};
        List<User> users = userMapper.findUserByArray1(ids);
        System.out.println(users.size());
        sqlSession.close();
    }