梳理 Mybatis(二)

写在前面

上一篇公众号《梳理 Mybatis(一)》大致介绍了Mybatis的概念、顺带回顾了Jdbc的操作、开发Dao的方法、SqlSessionFactory、以及mybatis全局配置文件主要的参数使用。有兴趣的小伙伴们可以点进去看一看。

今天主要来说说Mybatis具体映射,日志以及在实际开发中用需要注意的点。

目录结构

XML 映射文件

MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦。

cache – 对给定命名空间的缓存配置。

cache-ref – 对其他命名空间缓存配置的引用。

resultMap– 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。

sql – 可被其他语句引用的可重用语句块。

insert – 映射插入语句

update – 映射更新语句

delete – 映射删除语句

select – 映射查询语句


自动生成主键

  1. 如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把keyProperty设置到目标属性上就 OK 了。

    <insert id="insertAuthor" useGeneratedKeys="true"keyProperty="id">

     insert into Author (username,password,email,bio)

     values (#{username},#{password},#{email},#{bio})

    </insert>

  2. 对于不支持自动生成类型的数据库或可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另外一种方法来生成主键。

       --------->keyProperty:将查询到主键设置到parameterType指定对象的哪个属性中---->User-->id将插入数据的主键返回,返回到user对象中。

//(数据表中的 自动递增 勾上)
<insert id="insertUser" parameterType="mydata.User">
  <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
    select last_insert_id()
  </selectKey>
  //获取 最后一次设置的id
  Insert into user(name,birthday,sex,address) values(#{name},#{birthday},#{sex},#{address})
</insert>


输出映射

  1. resultType

        使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。     

<!-- mybatis-config.xml 中 -->
<typeAlias type="com.someapp.model.User" alias="User"/>


<!-- SQL 映射 XML 中 -->
<select id="selectUsers" resultType="User">
    select id, username, hashedPassword from some_table where id = #{id}
</select>

MyBatis会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。

<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 属性就行了。

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

     2. resultMap

        mybatis中使用resultMap完成高级输出结果映射。如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

<resultMap type="my.UserOrders" id="uq1">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="birthday" property="birthday"/>
    <result column="sex" property="sex"/>
    <result column="address" property="address"/>
    
    <!-- 集合映射 一对多 “多” -->
    <collection property="orders" ofType="my.Orders">
      <id column="oid" property="id"/>
      <result column="user_id" property="user_id"/>
      <result column="number" property="number"/>
      <result column="createtime" property="createtime"/>
      <result column="note" property="note"/>
    </collection>


          <!-- 联合查询 一对一 的映射-->
          <association property="items" javaType="pojo.Items">
            <id column="iid" property="id"/>
            <result column="name" property="name"/>
            <result column="price" property="price"/>
            <result column="detail" property="detail"/>
            <result column="pic" property="pic"/>
            <result column="createtime" property="createtime"/>
          </association>
  </resultMap>
  
  <select id="UserOrders" resultMap="uq1">
    Select user.*,orders.id oid,orders.user_id,orders.number,orders.createtime,orders.note
    from user,orders
    where user.id=orders.user_id
  </select>

    3. resultMap元素的概念视图。

constructor - 用于在实例化类时,注入结果到构造方法中

             idArg- ID 参数;标记出作为 ID 的结果可以帮助提高整体性能

             arg- 将被注入到构造方法的一个普通结果

id– 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

result– 注入到字段或 JavaBean 属性的普通结果

association – 一个复杂类型的关联;许多结果将包装成这种类型

嵌套结果映射 – 关联本身可以是一个resultMap 元素,或者从别处引用一个

collection – 一个复杂类型的集合

嵌套结果映射 – 集合本身可以是一个 resultMap 元素,或者从别处引用一个

discriminator – 使用结果值来决定使用哪个resultMap

          case – 基于某些值的结果映射

               嵌套结果映射 –case 本身可以是一个 resultMap 元素,因此可以具有相同的结构和元素,或者从别处引用一个

  • 构造方法

        构造方法注入允许你在初始化时为类设置属性的值而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBean 属性来完成注入,但有一些人更青睐于通过构造方法进行注入。constructor 元素就是为此而生的。

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

        为了将结果注入构造方法,MyBatis 需要通过某种方式定位相应的构造方法。

<constructor>
   <idArg column="id" javaType="int" name="id" />
   <arg column="age" javaType="_int" name="age" />
   <arg column="username" javaType="String" name="username" />
</constructor>
  • discriminator鉴别器

        它很像Java 语言中的 switch 语句。

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

        MyBatis 会从结果集中得到每条记录,然后比较它的 vehicle_type值。如果它匹配任意一个鉴别器的 case,就会使用这个 case 指定的结果映射。cars 和 vehicles 之间有关系,也就是 Car 是一个 Vehicle。因此,我们希望剩余的属性也能被加载。而这只需要一个小修改。

  //extends="vehicleResult"
<resultMap id="carResult" type="Car" extends="vehicleResult">
  <result property="doorCount" column="door_count" />
</resultMap>

缓存

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

  • 映射语句文件中的所有 select 语句的结果将会被缓存

  • 映射语句文件中的所有 insert、update和 delete 语句会刷新(清空)缓存

  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。

  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

        缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用@CacheNamespaceRef 注解指定缓存作用域。

        

  • 为什么需要使用缓存

    如果在极短时间内,做相同的查询,那么它们的结果很可能是相同,而访问一次数据库是非常消耗资源的。如果有一层缓存,将极大减少资源的消耗。

  • Mybatis的一级缓存

        mybatis的一级缓存是SqlSession其内部是通过一个hashmap来实现的,hashmap的键是数据库记录的主键每个SqlSession之间是相互独立的。

  • Mybatis的一级缓存的生命周期

    • MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

    • √如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用

      √如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用

      √SqlSession中执行了任何一个update操作(update()、delete()、insert()),都会清空PerpetualCache对象的数据,但是该对象可以继续使用。Ps:数据进行了增删改,那么这个map缓存就会被清空了,避免下次查询的时候脏读(就是数据被改掉了,你再从缓存中读的话,数据是不对的)

  • Mybatis的一级缓存的查询流程

  • ①对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果。

    ②判断从Cache中根据特定的key值取的数据是否为空,即是否命中。

    ③如果命中,则直接将缓存结果返回。

    ③如果没命中:

        1.去数据库中查询数据,得到查询结果

        2.将key和查询到的结果分别作为key,value对存储到Cache中

        3.将查询结果返回

  • Mybatis的二级缓存

  • 如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:

    二级缓存 ———> 一级缓存——> 数据库

    如果二级缓存中没有,查询一级缓存,如果在一级缓存中有,将查询的结果存入二级缓存,并换回给用户,如果没有查询数据库,并一级一级的返回存入缓存,在返回给用户。
  • Mybatis二级缓存的配置

  • MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

    MyBatis要求返回的POJO必须是可序列化的。也就是要求实现Serializable接口。

    ①MyBatis支持二级缓存的总开关全局配置变量参数 cacheEnabled=true

    <configuration>
        <settings>
            <!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
            <setting name="cacheEnabled" value="true" />
            .....
        </settings>
        ....
    </configuration>
    

    ②该select语句所在的Mapper,配置了<cache> 或<cached-ref>节点,才有效。

    <!--开启本mapper的namespace下的二级缓存-->
      <!--
            eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
            (1) LRU,最近最少使用的,一处最长时间不用的对象
            (2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
            (3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
            (4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,移除最长时间不用的对形象
            flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100000秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存。
            size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
            这里配置的是1024个对象
            readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
            办法修改缓存,他的默认值是false,不允许我们修改
        -->
      <cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"></cache>
      
      <resultMap type="result3.OrdersOrderDetail" id="uq3">
        <id column="oid" property="id"/>
        <result column="username" property="name"/>
        <collection property="details" ofType="result3.DetailItems">
          <id column="odid" property="id"/>
          <result column="orders_id" property="orders_id"/>
          <result column="items_id" property="items_id"/>
          <result column="items_num" property="items_num"/>
            <association property="items" javaType="pojo.Items">
              <id column="iid" property="id"/>
              <result column="name" property="name"/>
              <result column="price" property="price"/>
              <result column="detail" property="detail"/>
              <result column="pic" property="pic"/>
              <result column="createtime" property="createtime"/>
            </association>
        </collection>
      </resultMap>
      <!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭
        刷新二级缓存 flushCache="true"-->
      <select id="ordersItems" resultMap="uq3" useCache="true" flushCache="true">
        select 
          user.username,
          orders.id oid,
          orderdetail.id odid,orderdetail.orders_id,orderdetail.items_id,orderdetail.items_num,
          items.id iid,items.name,items.price,items.detail,items.pic,items.createtime
        from orders,user,orderdetail,items
        where 
         user.id=orders.user_id and 
        orders.id=orderdetail.orders_id and 
        orderdetail.items_id=items.id
        <!-- <where>
          <if test="value!=null and value!=''">
            and orders.id=#{value}
            and user.id=orders.user_id and 
              orders.id=orderdetail.orders_id and 
              orderdetail.items_id=items.id
          </if>
        </where> -->
      </select>
    

动态sql

if

choose (when, otherwise)

trim (where, set)

foreach

  • if

        这个元素可以被用来定义可重用的 SQL代码段,这些 SQL 代码可以被包含在其他语句中对sql进行灵活拼接、组装。

  • choose, when, otherwise

        MyBatis 提供了 choose 元素,它有点像Java 中的 switch语句。

提供了“title”就按“title”查找,提供了“author”就按“author”查找的情形,若两者都没有提供,就返回所有符合条件的 BLOG。

<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>
  • trim(where, set)

        ①where:查找

        至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句若语句的开头为“AND”或“OR”,where 元素也会将它们去除。(若只有一个满足 则把满足地那一个最前面的符号去除)

        避免:

        SELECT * FROM BLOG

        WHERE

        AND title like‘someTitle’

              <where>

                     //test里面 name sex都是来自包装好的类里面 的字段

                     //来自 parameterType="my.Q1"里的成员变量

                     <if test="name!=null andname!=''">

                      and user.id=orders.user_id

                      and username like '%${name}%'

                     </if>

                     <if test="sex!=null andsex!=''">

                      and sex=#{sex}

                     </if>

              </where>

          ②trim:添加

        通过自定义 trim 元素来定制 where 元素的功能。和 where 元素等价

        //prefixOverrides=”AND”    去除多余sql前面的AND

        //suffixOverrides=”,”  去除sql语句结尾多余的逗号.

          prefix="(" 前缀的内容suffix=")"后缀的内容

===========和上面where 元素等价==================

<trimprefix="WHERE" prefixOverrides="AND">

       <if test="state != null">

        state = #{state}

       </if>

       <if test="title != null">

        AND title like #{title}

       </if>

       <if test="author != null andauthor.name != null">

        AND author_name like #{author.name}

       </if>

</trim>                           

         ③Set:更新

            用于动态更新语句的解决方案叫做 set。set 元素可以用于动态包含需要更新的列,而舍去其它的。

<updateid="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 关键字,同时也会删掉无关的逗号。

  • foreach

    向sql传递数组List,mybatis使用foreach解析

<foreach collection="集合名或数组名" 
         item="本次迭代获取的元素-->值" 
         index="迭代次数(索引)-->键" 
         open="and id IN(" 
         close=")" 
         separator=",分隔符">
</foreach>

  • *script

        对于在带注释的映射器类中使用动态sql,可以使用script元素。

@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);
  • bind

    bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>


日志

  • 日志工具

    SLF4J

    ApacheCommons Logging

    Log4j2

    Log4j

    JDKlogging

    它会使用第一个查找得到的工具(按上面列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。如果你的应用部署在一个类路径已经包含 Commons Logging 的环境中,而你又想使用其它日志工具,你可以通过在MyBatis 配置文件 mybatis-config.xml里面添加一项 setting 来选择别的日志工具。

<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>
    ...
  </settings>
</configuration>

   logImpl可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,或者是实现了接口org.apache.ibatis.logging.Log 的,且构造方法是以字符串为参数的类的完全限定名

  • 日志配置

    再次说明下,具体怎么做,由使用的日志工具决定,这里以 Log4J 为例。配置日志功能非常简单。

步骤 1:添加 Log4J 的 jar 包

步骤 2:配置 Log4J

在应用的类路径中创建一个名称为 log4j.properties的文件,文件的具体内容如下:

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

添加以上配置后,Log4J 就会记录 org.mybatis.example.BlogMapper的详细执行操作。

//将日志的记录方式从接口级别切换到语句级别(只对 selectBlog 语句记录日志)

log4j.logger.org.mybatis.example.BlogMapper.selectBlog=TRACE

//对一组映射器接口记录日志

log4j.logger.org.mybatis.example=TRACE

//Mybatis 中 SQL 语句的日志级别被设为DEBUG

log4j.logger.org.mybatis.example=DEBUG

。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值