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


写在前面:十分感谢《深入浅出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>

猜你喜欢

转载自blog.csdn.net/w635614017/article/details/80047619
今日推荐