mybatis设置缓存

一、mybatis配置的简单介绍

本文介绍的是基于XML的配置,并不是关于注解的Mybatis配置。当然复杂SQL情况下都建议使用XML配置。

(1)配置步骤

  这里记录的只是Myabtis的简单配置,并没有证整合Spring等框架,所以相对简单。我开始学的时候也是反复记不住,不知道为什么要这么配置,这么配置的作用是什么。之后经过研读《深入浅出Mybatis基础原理与实战》(我这里只有PDF电子版本,有需要的朋友可以评论或者私信我),总结并画图让我对整个配置过程有了全新的认识。

  简单来说,Mybatis的配置主要分为以下几步(整合Spring之后有些就不需要了,但是一开始学习不建议直接整合Spring):

  • 编写POJO即JavaBean,最终的目的是将数据库中的查询结果映射到JavaBean上;
  • 配置与POJO对应的Mapper接口:里面有各种方法,对应mapper.xml中的查询语句;
  • 配置与POJO对应的XML映射:编写缓存,SQL查询等;
  • 配置mybatis-config.xml主要的Mybatis配置文件:配置数据源、扫描mapper.xml等。

  注意:以上的配置并没有严格的前后顺序;

(2)配置流程图

(3)配置总结

可以这么总结Mybatis或者帮助理解Mybatis的配置,我总结了以下三点提供参考:

  • 一切Mybatis配置都是为了创建SqlSession进行SQL查询;
  • 归根结底程序代码中我们屏蔽了各种配置映射,只显式调用使用Mapper接口,那么接口实现类的获得是通过SqlSession.getMapper()获得;
  • 那么mapper接口实现类的获得是通过mybatis-config.xml->SqlSessionFactoryBuilder->SqlSessionFacotry->SqlSession->mapper;

二、mybatis的生命周期

正确理解SqlSessionFactory、SqlSessionFactoryBuilder、SqlSession和Mapper的生命周期对于优化Mybatis尤为重要,这样可以使Mybatis高效正确完成;同为重要时Mybatis的生命周期对于理解Myabtis缓存的配置也尤为重要,我这里只做简单的文字介绍(其实也好理解):

 (1)SqlSessionFactoryBuilder:作用就是创建一个构建器,一旦创建了SqlSessionFactory,它的任务就算完成了,可以回收。

 (2)SqlSessionFactory:作用是创建SqlSession,而SqlSession相当于JDBC的一个Connection对象,每次应用程序需要访问数据库,我们就要通过SqlSessionFactory创建一个SqlSession,所以SqlSessionFactory在整Mybatis整个生命周期中(每个数据库对应一个SqlSessionFactory,是单例产生的)。

 (3)SqlSession:生命周期是存在于请求数据库处理事务的过程中,是一个线程不安全的对象(在多线程的情况下,需要特别注意),即存活于一个应用的请求和申请,可以执行多条SQL保证事务的一致性。

 (4)Mapper:是一个接口,并没有实现类它的作用是发送SQL,返回我们需要的结果,或者发送SQL修改数据库表,所以它存活于一个SqlSession内,是一个方法级别的东西。当SqlSession销毁的时候,Mapper也会销毁。

三、介绍mybatis缓存

(1)系统缓存:包括一级缓存与二级缓存

  一级缓存:默认情况下Myabtis对于同一个SqlSession开启一级缓存    

  • 在默认没有配置的情况下,只会开启一级缓存(只针对同一个SqlSession而言);
  • 在参数与SQL完全一样的情况下并且不声明刷新缓存没超时的,使用同一个SqlSession对象调用同一个Mapper方法时(SqlSession对象生命周期为方法级别),SqlSession只会取出当前缓存数据,不会再到数据库中进行查询;
  • 如果不同的SqlSession,即使同一个Mapper也会进行到数据库中进行不同的查询,即不同的SqlSession一级缓存是无效的。

  二级缓存:这里可以结合SqlSessionFactory等的生命周期能加深理解

  • 不同的SqlSession是隔离的,为了解决这个问题,我们可以在SqlSessionFactory层面上设置二级缓存提供各个对象SqlSession
  • 二级缓存默认是不开启的,需要进行配置,Mybatis要求返回的POJO必须是可序列化的,即POJO实现Serializable接口。

  缓存的配置只需要在XML配置即可,或者指定算法,刷新时间间隔,缓存状态,大小等

 <cache eviction="LRU" readOnly="true" flushInterval="100000" size="1024"></cache>

A. 映射语句文件中所有select语句将会被缓存;

B. 映射语句文件中所有insert、update和delete语句会被刷新缓存;

C. 缓存使用默认的LRU最近最少使用算法回收;

D. 根据时间表,缓存不会任何时间顺序刷新;

E. 缓存会存储列表集合或对象的1024个引用

F. 缓存被视为可read/write的缓存,意味着是不可以被共享的,而可以被安全地修改。

(2)自定义缓存:结合Redis等主流缓存配置

我们可以使用比如现在比较火的Redis缓存,需要实现Myabtis为我们提供的接口org.apache.ibatis.cache.Cache。虽然现在主流Mybatis用的都是自定义缓存,但是这里先不过多介绍,我一步一步来学习记录!

四、缓存代码实现

包结构图:

数据库user表:要与User对应,Mybatis会根据驼峰命名进行自动映射,即user表中id字段映射为User POJO中的id,如果使用的是插件生成,POJO就会自动对应。

mysql> use mybatis;
Database changed
mysql> select*from user;
+----+----------+
| id | name     |
+----+----------+
|  1 | Zhangsan |
|  2 | Lisi     |
+----+----------+
2 rows in set

(1)User.java: POJO

import java.io.Serializable;

/**
 * POJO:User
 * @author Lijian
 *
 */
public class User implements Serializable{

    private int id;
    private String name;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

(2)UserMapper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lijian.dao.UserMapper">
  <resultMap id="userMap" type="com.lijian.model.User">
       <id property="id" column="id"/>
    <result column="name" property="name" />
  </resultMap>
  <!-- 使用POJO映射结果集 -->
  <select id="findByUserId" parameterType="int" resultType="user">
      select * from user where id = #{id}
  </select>
  <!-- 使用resultMap映射结果集 -->
  <select id="findByUserName" parameterType="string" resultMap="userMap">
    select * from user where name = #{name}      
  </select>
</mapper>

(3)UserMapper.java:

public interface UserMapper {
    User findByUserId(int id);
    User findByUserName(String name);
}

(4)mybatis-config.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- mybatis-config.xml常用的配置及顺序:
        (properties?,
        settings?,
        typeAliases?,
        typeHandlers?,
        objectFactory?,
        objectWrapperFactory?,
        reflectorFactory?,
        plugins?,
        environments?,
        databaseIdProvider?,
        mappers?)
     -->

   <!-- jdbc数据库属性配置文件db.properties -->
     <properties resource="db.properties"></properties>
      <settings>
         <setting name="logImpl" value="LOG4J"/>
    </settings>
     <typeAliases>
         <typeAlias alias="user" type="com.lijian.model.User"/>
     </typeAliases>
     <!-- default 默认数数据源 -->
     <!-- environments配置:可以注册多个数据源DataSource,每个数据源分为两部分:一个是数据源的配置,另外一个是数据库事务配置。 -->
     <environments default="development">
         <!-- dataSource 1-->
         <environment id="development">
            <transactionManager type="JDBC">
                <!-- 关闭自动提交 -->
                <property name="autoCommit" value="false"/>
            </transactionManager>
             <!-- POOLED连接池数据库 -->
               <dataSource type="POOLED">
                   <property name="driver" value="${jdbc.driver}"/>
                   <property name="url" value="${jdbc.url}"/>
                   <property name="username" value="${jdbc.username}"/>
                   <property name="password" value="${jdbc.password}"/>
               </dataSource>
         </environment>
     </environments>
     <mappers>
         <!-- 可以通过包名导入映射器 :但mapper interface与mapper.xml必须在同一个包下-->
         <!-- <package name="com.lijian.mapper"/> -->
         <!-- 文件路径引入 -->
         <mapper resource="com/lijian/mapper/UserMapper.xml"/>
     </mappers>
</configuration>

(5)SqlSessionFactoryUtils:

/**
 * 创建SqlSession
 * @author Lijian
 * 创建顺序:mybatis-config.xml->SqlSessionFactoryBuilder->SqlSessionFactory(Singleton)->SqlSession
 *
 */
import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SqlSessionFactoryUtils {
    private static final Logger logger = LogManager.getLogger(SqlSessionFactoryUtils.class);
    
    //synchronized lock
    private static final Class CLASS_LOCK = SqlSessionFactoryUtils.class;
    private static SqlSessionFactory sqlSessionFactory = null;
    
    //private constructors
    private SqlSessionFactoryUtils(){};
    /**
     * 因为一个数据库对应一个SqlSessionFactory,所有采用单例模式生成SqlSessionFactory
     * @return SqlSessionFactory
     */
    public static SqlSessionFactory initSqlSessionFactory() {
        String config = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(config);
        }catch (IOException e) {
            logger.info("SqlSessionFactoryUtils");
        }
        synchronized (CLASS_LOCK) {
            if (sqlSessionFactory == null) {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
            
        }
        return sqlSessionFactory;
    }
    /**
     * openSession进行SQL查询
     * @return SqlSession
     */
    public static SqlSession openSession() {
        if (sqlSessionFactory == null) {
            initSqlSessionFactory();
        }
        return sqlSessionFactory.openSession();
    }
    
}

(6)MybatisMain.java:测试类

import org.apache.ibatis.session.SqlSession;

import com.lijian.dao.UserMapper;
import com.lijian.utils.SqlSessionFactoryUtils;

public class MybatisMain {
    public static void main(String[] args) {
        SqlSession sqlSession = null;
        SqlSession sqlSession2 = null;
        try {
            //获得SqlSession
            sqlSession = SqlSessionFactoryUtils.openSession();
            sqlSession2 = SqlSessionFactoryUtils.openSession();
            //获得Mapper:动态代理生成UserMapper实现类
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //默认一级缓存:相同SELECT与param,只查询一次
            System.out.println("=======================默认使用系统一级缓存=======================");
            userMapper.findByUserId(1);
            userMapper.findByUserId(1);
            //二级缓存commit才会有效
            sqlSession.commit();
            System.out.println("=======================重新创建SqlSession=======================");
            sqlSession2 = SqlSessionFactoryUtils.openSession();
            UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
            userMapper2.findByUserId(1);
            //二级缓存commit才会有效
            sqlSession2.commit();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        finally {
            if (sqlSession != null) {
                //sqlSession生命周期是随着SQL查询而结束的
                sqlSession.close();
            }
            if (sqlSession2 != null) {
                sqlSession2.close();
            }
        }
    }
}

注意UserMapper.xml当前是没有开启二级缓存的,故默认为一级缓存,如何得到证实呢?在MybatisMain中,我们创建了:

  • sqlSession:开启两个一模一样的SELECT SQL查询,即userMapper.findByUserId(1),那么如果一级缓存有效且开启的话,只会进行一次查询,之后有一次SQL语句日志输出;
  • sqlSession2:开启与sqlSession中一模一样的SELECT查询,如果二级缓存没有开启,一级缓存默认开启的话,是会进行查询的,会有一次SQL语句日志输出。

=======================默认使用系统一级缓存======================= [DEBUG][main][2018-07-29 21:45:41][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Preparing: select * from user where id = ? [DEBUG][main][2018-07-29 21:45:41][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Parameters: 1(Integer) [TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Columns: id, name [TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Row: 1, Zhangsan [DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Total: 1 =======================重新创建SqlSession======================= [DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Preparing: select * from user where id = ? [DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Parameters: 1(Integer) [TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Columns: id, name [TRACE][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Row: 1, Zhangsan [DEBUG][main][2018-07-29 21:45:42][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Total: 1

接下来我们开启二级缓存:在不同SqlSession中所有相同的SELECT语句将会被缓存,只会有一次SQL语句日志输出,并且会有Cache Hit Ratio缓存命中率

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lijian.dao.UserMapper">
  <!-- 开启二级缓存 :针对SqlSessionFactory,同时POJO必须实现Serializable接口
      (1)所有select会被缓存
      (2)insert、update、delete会刷新缓存
      (3)默认使用LRU
      (4)缓存是可read/write,不是共享的是可以被安全地修改
  -->
  <cache eviction="LRU" readOnly="true" flushInterval="100000" size="1024"></cache>
  <resultMap id="userMap" type="com.lijian.model.User">
       <id property="id" column="id"/>
    <result column="name" property="name" />
  </resultMap>
  <!-- 使用POJO映射结果集 -->
  <select id="findByUserId" parameterType="int" resultType="user">
      select * from user where id = #{id}
  </select>
  <!-- 使用resultMap映射结果集 -->
  <select id="findByUserName" parameterType="string" resultMap="userMap">
    select * from user where name = #{name}      
  </select>
</mapper>

日志如下:

=======================默认使用系统一级缓存======================= [DEBUG][main][2018-07-29 21:49:26][org.apache.ibatis.cache.decorators.LoggingCache] - Cache Hit Ratio [com.lijian.dao.UserMapper]: 0.0 [DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Preparing: select * from user where id = ? [DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - ==> Parameters: 1(Integer) [TRACE][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Columns: id, name [TRACE][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Row: 1, Zhangsan [DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.logging.jdbc.BaseJdbcLogger] - <== Total: 1 [DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.cache.decorators.LoggingCache] - Cache Hit Ratio [com.lijian.dao.UserMapper]: 0.0 =======================重新创建SqlSession======================= [DEBUG][main][2018-07-29 21:49:27][org.apache.ibatis.cache.decorators.LoggingCache] - Cache Hit Ratio [com.lijian.dao.UserMapper]: 0.3333333333333333

备注:

cache属性的简介:

eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。

(1) LRU(Least Recently Used),最近最少使用的,最长时间不用的对象

(2) FIFO(First In First Out),先进先出,按对象进入缓存的顺序来移除他们

(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象

(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
移除最长时间不用的对形象

flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当
SQL被执行的时候才会去刷新缓存。

size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象

readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值