美文网首页开源工具技巧程序员
自学Mybatis系列(3)——MyBatis的映射器

自学Mybatis系列(3)——MyBatis的映射器

作者: AceCream佳 | 来源:发表于2017-02-13 19:21 被阅读0次

    写在前面:十分感谢《深入浅出Mybatis技术原理与实战》这本书,大多数地方是书上的话,希望自己能在后面的文章中多写一些自己的理解。而且最重要的是!每次照书无脑敲的时候,都好羞愧啊(害羞脸)。后面一定注意这些问题。最后要感谢点赞、评论以及指正的朋友们,你们是坠吼的!!!


    Mapper 映射器

    映射器是个好东西,按照官方文档的话来说就是:

    The true power of MyBatis is in the Mapped Statements. This is where the magic happens.

    简单翻译就是:Mybatis真正的力量来源于它的映射语句,那里是见证奇迹的地方!
    Mapper XML文件相对简单。 如果你将它们与等效的JDBC代码进行比较,你会节省95%的代码!MyBatis的目的是专注于SQL,并尽力去帮我们节省时间和精力去做其他的部分。
    Mapper XML文件只有几个一级元素,介绍一下:

    • cache - 给定命名空间的缓存配置。
    • cache-ref - 从另一个命名空间引用缓存配置。
    • resultMap - 最复杂最也是最强大的元素,描述如何从数据库结果集中加载对象
    • parameterMap - 已过时!
    • sql - 可以被其他语句引用的SQL的可重用块。
    • insert - 映射INSERT语句。
    • update - 映射UPDATE语句。
    • delete - 映射DELETE语句。
    • select - 映射SELECT语句。

    我们将逐个击破!

    select

    select语句是我们在Mybatis中使用最多的语句。我们使用查询语句的频率高于我们使用修改语句的频率,对于插入,更新或删除,来说,他们中可能会包含着很多的选择。 直接举例子吧:

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

    此语句接受类型为int(或Integer)的参数,并返回Map类型的数据。
    注意参数符号:#{id}
    让我们看一下如果使用JDBC的话代码是什么样子的:

    // JDBC代码,不是Mybatis
          String sql = "SELECT * FROM PERSON WHERE ID=?";
          PreparedStatement ps = conn.prepareStatement(sql);
          ps.setInt(1,id);
    

    可以想象,如果字段很多的话,传统方式下我们将会在书写很多重复的代码,这样不仅很枯燥,还很容易出错。例如:

    <select
      id="selectPerson"
      parameterType="int"
      parameterMap="deprecated"
      resultType="hashmap"
      resultMap="personResultMap"
      flushCache="false"
      useCache="true"
      timeout="10000"
      fetchSize="256"
      statementType="PREPARED"
      resultSetType="FORWARD_ONLY">
    

    逐个进行解释:

    • id:它和Mapper命名空间组合起来是唯一的。
    • parameterType:可以给出类的全名吗,也可以给类的别名,但是必须是内部定义或者自定义的。
    • parameterMap:即将废弃。
    • resultType:返回值的类型,也可以使用别名,不能和resultMap同时使用!
    • resultMap:可以执行强大的映射功能,解决很多的映射困难,不能与resultType同时使用。resultMap给了我们自己定义映射规则的机会。
    • flushCache:如果设置true将会清空本地缓存和二级缓存。默认为false。
    • useCache:默认为true,是启动二级缓存的开关。
    • timeout:设置超时参数,等超时抛出异常,单位是秒。默认值由厂商提供。
    • fetchSize:设置获取记录的总条数。
    • statementType:设置使用哪个JDBC的Statement工作。可以取值:STATEMENT、PREPARED。默认值为PREPARED。
      -resultSetType:取值为FORWARD_ONLY 、SCROLL_SENSITIVE 、SCROLL_INSENSITIVE。默认值为unset(取决于驱动程序)。

    给映射器传递多个参数有三种方式:
    1.使用Map传参。
    2.使用注解方式传参。
    3.使用JavaBean传参。
    首先,使用Map传参会导致业务可读性丧失,很难继续扩展和维护,这里很不推荐这种方式。
    其次,使用@Param注解传递多个参数,这种方式的使用受到参数个数的影响,如果参数个数很多的话,那要写n多个@Param吗?所以一般来说,如果参数个数在五个以下的话,使用这种方式最好。
    最后,如果参数多于五个,我们使用JavaBean。

    insert、update、delete

    先粘个代码,展示一下元素,官方文档的代码,感受一下:

    <insert
      id="insertAuthor"
      parameterType="domain.blog.Author"
      flushCache="true"
      statementType="PREPARED"
      keyProperty=""
      keyColumn=""
      useGeneratedKeys=""
      timeout="20">
    <update
      id="updateAuthor"
      parameterType="domain.blog.Author"
      flushCache="true"
      statementType="PREPARED"
      timeout="20">
    <delete
      id="deleteAuthor"
      parameterType="domain.blog.Author"
      flushCache="true"
      statementType="PREPARED"
      timeout="20">
    

    具体都是干啥的,这里真的懒得写了,虽然元素很多,但是能用上的没几个。懂SQL的童鞋看了示例马上就能学会:

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

    没错,官方的文档就是这么粗暴。我们主要讨论需要去重点处理的问题——主键回填和自定义
    首先,我们可以使用keyProperty属性指定那个是主键字段,同时使用userGeneratedKeys属性设置这个主键是否使用数据库的内置策略生成。
    举个栗子:

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

    这里就是指定了id为自增字段。我们建立POJO,提供getter和setter方法,就可以使用主键回填了。
    但是,有时候我们需要根据一些特殊的关系设置主键id的值,比如我们要求:如果表t_role没有记录,我们设置id=1,否则我们取最大id加2来设置新的主键,这时候我们就需要这么写:

    <insert id="insertRole" parameterType="role" userGeneratedKeys="true" keyProperty="id">
          <selectKey keyProperty="id" resultType="int" order="BEFORE">
            select if(max(id) is null,1,max(id)+2) as newId from t_role
          </selectKey>
          insert into t_role
            (id, role_name,note)
          values
            (#{id}, #{rolename}, #{note})
        </insert>
    

    sql

    此元素可用于定义可以包含在其他语句中的SQL代码的可重用片段。 它可以是静态的(在加载阶段)参数化。 不同的属性值在include实例中可能不同。 例如:

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

    上面我们用sql元素定义了userColumns,他可以很方便的使用include元素的refid属性引用,达到重用的功能。请看:

      <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 t1
             cross join t2
        </select>
    

    我们也可以给refid参数值

    <sql id="someinclude">
      select * from <include refid="${tableName}" />
    </sql>
    

    这样实现了定义一处,很多地方也可以引用。


    参数

    对于储存过程来说,有三种参数:输入参数、输出参数、输入输出参数。大部分情况下Mybatis回去推断返回数据的类型,所以大部分情况下是不要去配置参数类型和结果类型的。但返回为空的字段类型是需要去设置的,因为Mybatis无法判断null是什么类型!
    关于参数我想说一下特殊字符替换和处理的问题(#{}和${})
    这里我要说两点:
    1.#{}方式能够很大程度防止sql注入。
    2.排序时使用order by 动态参数时需要注意,用$而不是#
    3.尽量都使用#{}

    resultMap

    resultMap元素是MyBatis中最重要和最强大的元素。 就是有了这个东西才允许我们节省90%的代码。JDBC需要从ResultSets检索数据,也就是说,因为resultMap在某些情况下Mybatis允许你做的事情,JDBC并不支持。
    resultMap定义的主要是一个结果集的映射关系。MyBatis现有的版本只支持查询。resultMap很复杂,在这只能简单列一下它的构成,具体的得我深入了解以后,再单独开一个番外去研究。
    resultMap里的元素:

    <resultMap>
        <constructor> <!-- 配置构造方法 -->
            <idArg/>
            <arg/>
        </constructor>
        <id/>  <!-- 定义主键 -->
        <result/>   <!-- 定义普通列的映射关系 -->
        <association/>    <!-- 一对一级联 -->
        <collection/>     <!-- 一对多级联 -->
        <discriminator>  <!-- 鉴别器级联 -->
            <case/>  <!-- 类似switch/case -->
        </discriminator>
    </resultMap>
    

    相关文章

      网友评论

        本文标题:自学Mybatis系列(3)——MyBatis的映射器

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