美文网首页
Mapper XML 文件

Mapper XML 文件

作者: 淡然_匆匆 | 来源:发表于2018-11-21 11:53 被阅读0次

    MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。

    SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

    • cache – 给定命名空间的缓存配置。
    • cache-ref – 其他命名空间缓存配置的引用。
    • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
    • parameterMap已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
    • sql – 可被其他语句引用的可重用语句块。
    • insert – 映射插入语句
    • update – 映射更新语句
    • delete – 映射删除语句
    • select – 映射查询语句

    下一部分将从语句本身开始来描述每个元素的细节。

    select

    查询语句是 MyBatis 中最常用的元素之一,光能把数据存到数据库中价值并不大,如果还能重新取出来才有用,多数应用也都是查询比修改要频繁。对每个插入、更新或删除操作,通常对应多个查询操作。这是 MyBatis 的基本原则之一,也是将焦点和努力放到查询和结果映射的原因。简单查询的 select 元素是非常简单的。比如:

    <select id="selectPerson" parameterType="int" resultType="hashmap">
      SELECT * FROM PERSON WHERE ID = #{id}
    </select>
    

    这个语句被称作 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
    注意参数符号:

    #{id}
    

    这就告诉 MyBatis 创建一个预处理语句参数,通过 JDBC,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:

    // Similar JDBC code, NOT MyBatis…
    String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
    PreparedStatement ps = conn.prepareStatement(selectPerson);
    ps.setInt(1,id);
    

    当然,这需要很多单独的 JDBC 的代码来提取结果并将它们映射到对象实例中,这就是 MyBatis 节省你时间的地方。

    insert, update 和 delete

    下面就是 insert,update 和 delete 语句的示例:

    <insert id="insertAuthor">
      insert into Author (id,username,password,email,bio)
      values (#{id},#{username},#{password},#{email},#{bio})
    </insert>
    
    <update id="updateAuthor">
      update Author set
        username = #{username},
        password = #{password},
        email = #{email},
        bio = #{bio}
      where id = #{id}
    </update>
    
    <delete id="deleteAuthor">
      delete from Author where id = #{id}
    </delete>
    

    如前所述,插入语句的配置规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理主键的生成,而且有多种生成方式。
    首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了。例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句可以修改为:

    <insert id="insertAuthor" useGeneratedKeys="true"
        keyProperty="id">
      insert into Author (username,password,email,bio)
      values (#{username},#{password},#{email},#{bio})
    </insert>
    

    sql

    这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:

    <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
    

    这个 SQL 片段可以被包含在其他语句中,例如:

    <select id="selectUsers" resultType="map">
      select
        <include refid="userColumns"><property name="alias" value="t1"/></include>,
        <include refid="userColumns"><property name="alias" value="t2"/></include>
      from some_table t1
        cross join some_table t2
    </select>
    

    参数(Parameters)

    前面的所有语句中你所见到的都是简单参数的例子,实际上参数是 MyBatis 非常强大的元素,对于简单的做法,大概 90% 的情况参数都很少,比如:

    <select id="selectUsers" resultType="User">
      select id, username, password
      from users
      where id = #{id}
    </select>
    

    Result Maps

    resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
    你已经见过简单映射语句的示例了,但没有明确的 resultMap。比如:

    <select id="selectUsers" resultType="map">
      select id, username, hashedPassword
      from some_table
      where id = #{id}
    </select>
    

    上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。 虽然在大部分情况下都够用,但是 HashMap 不是一个很好的领域模型。 你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 对象)作为领域模型。 MyBatis 对两者都支持。看看下面这个 JavaBean:

    package com.someapp.model;
    public class User {
      private int id;
      private String username;
      private String hashedPassword;
      
      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 getHashedPassword() {
        return hashedPassword;
      }
      public void setHashedPassword(String hashedPassword) {
        this.hashedPassword = hashedPassword;
      }
    }
    

    基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。
    这样的一个 JavaBean 可以被映射到 ResultSet,就像映射到HashMap 一样简单。

    <select id="selectUsers" resultType="com.someapp.model.User">
      select id, username, hashedPassword
      from some_table
      where id = #{id}
    </select>
    

    类型别名是你的好帮手。使用它们,你就可以不用输入类的完全限定名称了。比如:

    <!-- In mybatis-config.xml file -->
    <typeAlias type="com.someapp.model.User" alias="User"/>
    
    <!-- In SQL Mapping XML file -->
    <select id="selectUsers" resultType="User">
      select id, username, hashedPassword
      from some_table
      where id = #{id}
    </select>
    

    这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。如果列名和属性名没有精确匹配,可以在 SELECT 语句中对列使用别名(这是一个 基本的 SQL 特性)来匹配标签。比如:

    <select id="selectUsers" resultType="User">
      select
        user_id     as "id",
        user_name     as "userName",
        hashed_password     as "hashedPassword"
      from some_table
      where id = #{id}
    </select>
    

    ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。 上面这些简单的示例根本不需要下面这些繁琐的配置。 出于示范的原因,让我们来看看最后一个示例中,如果使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。

    <resultMap id="userResultMap" type="User">
      <id property="id" column="user_id" />
      <result property="username" column="user_name"/>
      <result property="password" column="hashed_password"/>
    </resultMap>
    

    引用它的语句使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:

    <select id="selectUsers" resultMap="userResultMap">
      select user_id, user_name, hashed_password
      from some_table
      where id = #{id}
    </select>
    

    高级结果映射

    MyBatis 创建的一个想法是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们不总都是这样。 如果有一个独立且完美的数据库映射模式,所有应用程序都可以使用它,那就太好了,但可惜也没有。 ResultMap 就是 MyBatis 对这个问题的答案。
    比如,我们如何映射下面这个语句?

    <!-- Very Complex Statement -->
    <select id="selectBlogDetails" resultMap="detailedBlogResultMap">
      select
           B.id as blog_id,
           B.title as blog_title,
           B.author_id as blog_author_id,
           A.id as author_id,
           A.username as author_username,
           A.password as author_password,
           A.email as author_email,
           A.bio as author_bio,
           A.favourite_section as author_favourite_section,
           P.id as post_id,
           P.blog_id as post_blog_id,
           P.author_id as post_author_id,
           P.created_on as post_created_on,
           P.section as post_section,
           P.subject as post_subject,
           P.draft as draft,
           P.body as post_body,
           C.id as comment_id,
           C.post_id as comment_post_id,
           C.name as comment_name,
           C.comment as comment_text,
           T.id as tag_id,
           T.name as tag_name
      from Blog B
           left outer join Author A on B.author_id = A.id
           left outer join Post P on B.id = P.blog_id
           left outer join Comment C on P.id = C.post_id
           left outer join Post_Tag PT on PT.post_id = P.id
           left outer join Tag T on PT.tag_id = T.id
      where B.id = #{id}
    </select>
    

    你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写, 有很多的博文,每篇博文有零或多条的评论和标签。 我们来看看下面这个完整的例子,它是一个非常复杂的 ResultMap (假设作者,博客,博文,评论和标签都是类型的别名)。 不用紧张,我们会一步一步来说明。 虽然它看起来令人望而生畏,但其实非常简单。

    <!-- 超复杂的 Result Map -->
    <resultMap id="detailedBlogResultMap" type="Blog">
      <constructor>
        <idArg column="blog_id" javaType="int"/>
      </constructor>
      <result property="title" column="blog_title"/>
      <association property="author" javaType="Author">
        <id property="id" column="author_id"/>
        <result property="username" column="author_username"/>
        <result property="password" column="author_password"/>
        <result property="email" column="author_email"/>
        <result property="bio" column="author_bio"/>
        <result property="favouriteSection" column="author_favourite_section"/>
      </association>
      <collection property="posts" ofType="Post">
        <id property="id" column="post_id"/>
        <result property="subject" column="post_subject"/>
        <association property="author" javaType="Author"/>
        <collection property="comments" ofType="Comment">
          <id property="id" column="comment_id"/>
        </collection>
        <collection property="tags" ofType="Tag" >
          <id property="id" column="tag_id"/>
        </collection>
        <discriminator javaType="int" column="draft">
          <case value="1" resultType="DraftPost"/>
        </discriminator>
      </collection>
    </resultMap>
    

    resultMap 元素有很多子元素和一个值得讨论的结构。 下面是 resultMap 元素的概念视图。

    resultMap
    • constructor - 用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
    • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
    • result – 注入到字段或 JavaBean 属性的普通结果
    • association – 一个复杂类型的关联;许多结果将包装成这种类型
      • 嵌套结果映射 – 关联可以指定为一个 resultMap 元素,或者引用一个
    • collection – 一个复杂类型的集合
      • 嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个
    • discriminator – 使用结果值来决定使用哪个 resultMap
      • case – 基于某些值的结果映射
        • 嵌套结果映射 – 一个 case 也是一个映射它本身的结果,因此可以包含很多相 同的元素,或者它可以参照一个外部的 resultMap。

    相关文章

      网友评论

          本文标题:Mapper XML 文件

          本文链接:https://www.haomeiwen.com/subject/ijgqqqtx.html