[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>
相关文章
相关标签/搜索