[03] mapper.xml的基本元素概述


一、select

咱們基於這個持久層接口 GirlDao:
public interface GirlDao {

    List<Girl> findByAge(int age);

    Girl findById(long id);

    int insertGirl(Girl girl);

    int updateGirl(Girl girl);

    int deleteGirl(long id);
}

關於select,咱們仍是先使用最早提到的簡單的mapper的一個例子:
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
    <select id="findById" parameterType="long" resultType="dulk.learn.mybatis.pojo.Girl">
        SELECT * FROM girl WHERE id = #{id}
    </select>
</mapper>

這裏的 <select> 標籤,即表明的是 select 操做,你應該能夠聯想到,咱們固然還有 <insert>、<update>、<delete> 標籤。縱觀這段內容,須要提醒你知道的是:
  • select 標籤的 id 屬性用來標記和區別該 select,若是須要對應接口則 id 和接口方法名要一致
  • parameterType 表示輸入參數的類型
  • resultType 表示輸出結果的類型
  • #{ } 表示傳入的動態參數的佔位符

簡單來講就是,這個語句叫 findById,接受long(或Long)類型參數(#{id} 則說明參數名爲id),返回Girl類型的對象。其實這也至關於告訴MyBatis建立一個預處理語句對象,並傳入相應的值,因此以上又至關於JDBC的以下操做:
String findById = "SELECT * FROM girl WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(findById);
ps.setLong(1, id);

1.1 輸入參數 parameterType

在如上的示例中咱們已經看到,輸入參數的屬性爲 parameterType,其值是將會傳入這條語句的參數類的 「 徹底限定名」 或 「 別名」(還記得別名嗎?參見mybatis-config.xml全局配置文件說明中的typeAliases)。

另外可喜的是,這個屬性實際上是可選的,由於 MyBatis 能夠經過 TypeHandler 推斷出具體傳入語句的參數,因此這個屬性的默認值是 unset,固然,每每咱們在開發的時候仍是主動進行了限定,由於不少時候咱們傳入的將會是一個封裝了參數的類。

上面的含義是說,假如你傳入的是一個自定義的對象,那麼佔位符的屬性會在該對象屬性中進行查找並賦值:
<insert id="insertUser" parameterType="User">
    insert into users (id, username, password)
    values (#{id}, #{username}, #{password})
</insert>

如上若 User 類型的參數對象傳遞到了語句中,id、username 和 password 屬性將會被查找,而後將它們的值傳入預處理語句的參數中。

1.2 輸出結果 resultType / resultMap

輸出結果有兩種形式來進行限定,即你要麼使用 resultType,要麼使用 resultMap,二者不能同時出如今相同的語句標籤中:
  • resultType - 返回的指望類型的類的徹底限定名或別名(注:如果集合情形,那應該是集合可包含的類型,而不能是集合自己)
  • resultMap - 外部 resultMap 的命名引用

resultType很好理解,其實就和parameterType的使用性質是同樣的。那麼來簡單說明一下這裏的resultMap和其外部命名引用是什麼意思。

其實在select標籤以外,有一個同級的標籤,也叫 resultMap(注意咱們剛纔提到的resultMap是做爲select標籤的屬性出現的),該標籤主要針對一些複雜的結果映射,用來 「 描述語句和對象之間的映射關係」,你能夠理解爲咱們手動地匹配查詢出的列名和對象的屬性之間的對應關係(注意是「查詢出的列名」而非數據庫本來的列名,這意味着你語句中是否有使用「AS」關鍵字會相應對該映射關係的描述形成影響)。

仍是以前的mapper的例子,用resultMap來配置的話,就應該是以下面貌:
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
    <!--定義resultMap-->
    <resultMap id="girlResultMap" type="dulk.learn.mybatis.pojo.Girl">
        <!--id表查詢結果中的惟一標識-->
        <id property="id" column="id" />
        <!--result表示對普通列名的映射,propertyi表對象屬性名,column表查詢出的列名-->
        <result property="age" column="age" />
    </resultMap>

    <!--引用外部resultMap,即girlResultMap-->
    <select id="findById" parameterType="long" resultMap="girlResultMap">
        SELECT * FROM girl WHERE id = #{id}
    </select>
</mapper>

因此,若是你是使用ResultType做爲輸出映射,只有查詢出來的列名和對象屬性名一致纔可映射成功。不然,你就須要定義一個resultMap來描述二者之間的關係。

另外值得一提的是, 對於生成的結果是單個仍是集合,每每是根據對應接口的返回值類型來肯定的,假如 GirlDao 中 List<Girl> findByAge(int age),則返回集合(MyBatis內部調用selectList方法);而諸如 Girl findById(long id) 返回單個對象調用(MyBatis內部調用selectOne方法),因此哪怕你知道你sql會返回的是多個對象,你在 resultType 中也只須要定義的是單個元素的類型,而不是集合。

二、insert, update 和 delete

咱們已經知道了<select>,觸類旁通來看,其實 <insert>、<update>、<delete> 也就不難使用了,以下例:
<!--使用了useGeneratedKeys和keyProperty來將生成的主鍵設置到對象屬性中-->
<insert id="insertGirl" parameterType="dulk.learn.mybatis.pojo.Girl" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO girl (age)
    VALUES (#{age})
</insert>

<update id="updateGirl" parameterType="dulk.learn.mybatis.pojo.Girl">
    UPDATE girl
    SET age = #{age}
    WHERE id = #{id}
</update>

<delete id="deleteGirl" parameterType="long">
    DELETE FROM girl
    WHERE id = #{id}
</delete>

只是須要注意的是:
  • insert / update / delete 都沒有 resultType 或 resultMap,他們的返回值是受影響的行數
  • 若數據庫支持自動生成主鍵,可設置 useGeneratedKeys 爲 true,並使用 keyProperty 將生成的主鍵值設置到目標屬性上(如上例的insert)

作了個單元測試以下:
public class Test {

    @org.junit.Test
    public void testMyBatis() throws IOException {
        //讀取配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //獲取工廠類
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        //獲取SqlSession數據庫會話對象
        SqlSession sqlSession = factory.openSession();
        //獲取Dao
        GirlDao girlDao = sqlSession.getMapper(GirlDao.class);

        Girl girl = new Girl();
        girl.setAge(18);

        //insert a girl with age 18
        int resultInsert = girlDao.insertGirl(girl);
        Assert.assertEquals(1, resultInsert);
        Assert.assertEquals(18, girlDao.findById(girl.getId()).getAge());

        //update girl's age from 18 to 20
        girl.setAge(20);
        int resultUpdate = girlDao.updateGirl(girl);
        Assert.assertEquals(1, resultUpdate);
        Assert.assertEquals(20, girlDao.findById(girl.getId()).getAge());

        //delete girl
        int resultDelete = girlDao.deleteGirl(girl.getId());
        Assert.assertEquals(1, resultDelete);
        Assert.assertNull(girlDao.findById(girl.getId()));
    }
    
}

三、sql

sql元素用來定義可重用的SQL代碼片斷,其餘語句能夠用過 <include> 標籤來將之包含其中。看個例子就能明白了:
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">

    <resultMap id="girlResultMap" type="dulk.learn.mybatis.pojo.Girl">
        <id property="id" column="id" />
        <result property="age" column="age" />
        <result property="cupSize" column="cup_size" />
    </resultMap>

    <!--定義可重用的sql片斷-->
    <sql id="girlColumn">
      id, age
    </sql>

    <select id="findById" parameterType="long" resultMap="girlResultMap">
        <!--經過include引用外部sql片斷-->
        SELECT <include refid="girlColumn" />
        FROM girl WHERE id = #{id}
    </select>

</mapper>
相關文章
相關標籤/搜索