重新认识Mybatis系列(二)

重新认识Mybatis(二)

Mybatis的XML映射文件映射到具体的Mapper接口,找到对应的Sql语句执行命令。映射器的XML文件十分简单,只有几个重要标签元素

基本crud标签

查询是Mybatis中使用频率最高的语句

<select id="findBook" parameterType="int" resultType="hashmap">
        select id,name,price from book where id=#{id}
</select>

标签的属性id为映射器的方法名,parameterType属性为方法参数类型别名,resultType属性为返回结果类型别名,Mybatis为基本类型和包装类型,集合类型等简单的数据类型都内置了默认别名,例如,HashMap -> hashmap,Integer ->int等,规则也很简单,将字母变小写即可。

select标签主要属性如下

在这里插入图片描述

insert,update,delete标签的主要属性如下

在这里插入图片描述

sql标签

sql标签可以定义可重用的sql语句

<sql id="sqlid"> ${b}.id,${b}.name,${b}.price </sql>
<select id="findBook">
	select 
  	<include refid="sqlid"><property name="b" value="book"></property></include>
     from book
</select>

include标签的refid属性可以使用不同的sql标签

参数
<select id="findBooks" parameterType="int" resultType="book">
  	select id,name,price from book where id=#{id,javaType=int,jdbcType=NUMERIC}
</select>

javaType指定java类型,Mybatis其实会帮我们确定javaType的值,除非参数对象是一个HashMap。这个时候必须指定javaType类型

JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)

#{price,javaType=double,jdbcType=NUMERIC,numericScale=2}

指定price参数的小数点保留2位

字符#{} 会被解析为?占位符,但有时我们既想从参数取得动态数据,又不想让它解析为?占位符,仅仅解析成普通字符串,这时可以用${},但是这样并不安全

@Select("select * from book where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

它会将column属性解析为字符串拼接到sql语句

结果映射

<resultMap id="bookResultMap" type="Book">
  <id property="id" column="book_id" />
  <result property="name" column="book_name"/>
  <result property="price" column="book_price"/>
</resultMap>

<select id="findBooks" resultMap="bookResultMap">
  select id, name, price
  from book
  where book_id = #{id}
</select>

会将从数据库取出的相应列如book_id的值自动封装到Book对象的id属性中

复杂的结果映射

在这里插入图片描述

id & result标签

id和result标签都将一个列的值映射到一个简单的数据类型上,id一般对应主键。它们是返回类型对象的两个属性。

在这里插入图片描述

构造方法

有时候需要从数据库中取得数据后封装为对象,但不想改变该对象的属性,可以使用私有属性,也可以使用构造方法注入。

这是一个对象的构造方法

public class User {
   //...
   public User(Integer id, String username, int age) {
     //...
  }
//...
}

<constructor>
   <idArg column="id" javaType="int"/>
   <arg column="username" javaType="String"/>
   <arg column="age" javaType="int"/>
</constructor>

使用constructor标签可以将这些数据封装进构造方法内

id,result标签和constructor标签都是将数据封装进对象,前者通过直接和属性名映射,后者通过构造方法映射。

关联
<association property="author" column="book_author_id" javaType="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
</association>

association标签处理“有一个”类型的关系,比如一本书有一作者。

property表示属性名,column表示查找的作者id列名。

该标签会为book对象映射author属性

<resultMap id="bookResult" type="Book">
  <id property="id" column="book_id" />
  <result property="name" column="book_name"/>
  <association property="author" column="book_author_id" javaType="Author" resultMap="authorResult"/>
</resultMap>

<resultMap id="authorResult" type="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
</resultMap>

上述配置就是在映射Book对象属性时,将id,result标签的id和name属性映射好后,再找到association标签中的author属性,再将其对应的resultMap中的属性映射进Author对象,再将Author对象映射进Book对象中。

集合
<resultMap id="bookResult" type="Book">
  <id property="id" column="book_id" />
  <result property="title" column="book_title"/>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <result property="body" column="post_body"/>
  </collection>
</resultMap>

一个博客只有一个作者(Author)。但一个博客“有多个”文章(Post)。

collection标签定义了Book对象的集合属性posts,代表有多个Post对象,ofType表示集合内部的数据类型

id,result和构造方法只映射普通属性,关联和集合对应“有一个”和“有多个”的对象关系

鉴别器
<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>

一个鉴别器的定义需要指定 column 和 javaType 属性。column 指定了 MyBatis 查询被比较值的地方

在这个示例中,MyBatis 会从结果集中得到每条记录,然后比较它的 vehicle type 值。 如果它匹配任意一个鉴别器的 case,就会使用这个 case 指定的结果映射。 如果不能匹配任何一个 case,MyBatis 就只会使用鉴别器块外定义的结果映射

自动映射

有三种自动映射等级:

  • NONE - 禁用自动映射。仅对手动映射的属性进行映射。
  • PARTIAL - 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射
  • FULL - 自动映射所有属性。

默认值是 PARTIAL,这是有原因的。当对连接查询的结果使用 FULL 时,连接查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。

无论设置的自动映射等级是哪种,你都可以通过在结果映射上设置 autoMapping 属性来为指定的结果映射设置启用/禁用自动映射。

缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

select语句的结果将被缓存,insert,update,delete语句会刷新缓存

cache-ref

对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。 但你可能会想要在多个命名空间中共享相同的缓存配置和实例。要实现这种需求,你可以使用 cache-ref 元素来引用另一个缓存。

<cache-ref namespace="com.codesy.OtherMapper"/>

动态SQL

使用动态SQL可以降低拼凑sql语句时的困难,通过一些标签让Mybatis轻松帮我们拼凑出复杂的sql语句

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
if标签
<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

title不为空就会将if标签内容拼接到末尾

choose、when、otherwise标签
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

着三个标签组合起来使用就像switch语句一样,when标签内为真即拼接此标签内容,并且只拼接这一条,其他略过不比较。

trim、where、set标签
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

where标签只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where标签也会将它们去除。

如果 where标签与你期望的不太一样,你也可以通过自定义 trim 标签来定制 where 标签的功能。比如,和 where 标签等价的自定义 trim 标签为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

用于更新的set标签

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

set标签会动态地在行首插入 SET 关键字,并会删掉额外的逗号

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>
foreach标签

对集合进行遍历使用foreach标签

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select

它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

在标签<>内部使用参数属性时,不用加#{}

script标签

如果使用注解形式开发,那么原本的xml标签可以在注解上这样写

 @Update({"<script>",
      "update Author",
      "  <set>",
      "    <if test='username != null'>username=#{username},</if>",
      "    <if test='password != null'>password=#{password},</if>",
      "    <if test='email != null'>email=#{email},</if>",
      "    <if test='bio != null'>bio=#{bio}</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateAuthorValues(Author author);

在字符串首尾用script标签包裹,并在外层用一对{}括号。

bind标签

有时候我们在拼接Sql语句时需要一个属性,但它并不来自于参数,这时可以用bind标签

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="MyPattern..." />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

这样定义好的pattern属性可以被#{}解析

创建SqlSession的方法

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

autoCommit 可选参数传递 true 值即可开启自动提交功能。若要使用自己的 Connection 实例,传递一个 Connection实例给 connection 参数即可。注意,我们没有提供同时设置 ConnectionautoCommit 的方法,这是因为 MyBatis 会依据传入的 Connection 来决定是否启用 autoCommit。对于事务隔离级别,MyBatis 使用了一个 Java 枚举包装器来表示,称为 TransactionIsolationLevel,事务隔离级别支持 JDBC 的五个隔离级别(NONEREAD_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE)。

  • ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。
  • ExecutorType.REUSE:该类型的执行器会复用预处理语句。
  • ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果 SELECT 在多个更新中间执行,将在必要时将多条更新语句分隔开来,以方便理解。

SqlSession的方法

SqlSession 在 MyBatis 中是非常强大的一个类。它包含了所有执行语句、提交或回滚事务以及获取映射器实例的方法。

语句执行方法
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

selectOne 和 selectList 的不同仅仅是 selectOne 必须返回一个对象或 null 值。如果返回值多于一个,就会抛出异常。如果你不知道返回对象会有多少,请使用selectList。如果需要查看某个对象是否存在,最好的办法是查询一个 count 值(0 或 1)。selectMap 稍微特殊一点,它会将返回对象的其中一个属性作为 key 值,将对象作为 value 值,从而将多个结果集转为 Map 类型值。由于并不是所有语句都需要参数,所以这些方法都具有一个不需要参数的重载形式。

游标(Cursor)与列表(List)返回的结果相同,不同的是,游标借助迭代器实现了数据的惰性加载。

立即批量更新方法
List<BatchResult> flushStatements()

当你将 ExecutorType 设置为 ExecutorType.BATCH 时,可以使用这个方法清除(执行)缓存在 JDBC 驱动类中的批量更新语句。

事务控制方法
void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)

默认情况下 MyBatis 不会自动提交事务,除非它侦测到调用了插入、更新或删除方法改变了数据库。如果你没有使用这些方法提交修改,那么你可以在 commit 和 rollback 方法参数中传入 true 值,来保证事务被正常提交(注意,在自动提交模式或者使用了外部事务管理器的情况下,设置 force 值对 session 无效)。大部分情况下你无需调用 rollback(),因为 MyBatis 会在你没有调用 commit 时替你完成回滚操作。不过,当你要在一个可能多次提交或回滚的 session 中详细控制事务,回滚操作就派上用场了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值