myBatis缓存

 

  • MyBatis

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架, 避免了几乎所有的 JDBC 代码和手动设置参数以及对结果集的检索封装。MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。

 

功能架构分为三层:

 

  1. 基础支撑层:负责最基础的功能支撑,包括连接管理(session)、事务管理(mybatis将jdbc自动提交事务改为了手动提交,封装了transactionManger和session)、配置加载(config.xml,mapper.xml)和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件,为上层的数据处理层提供最基础的支撑。
  2. 数据处理层负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
  3. API接口层:提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

二、SqlSessionFactoryBuilder:这个类可以被实例化、使用和丢弃。一旦创建好SqlSessionFactory,就不再需要它了。因此它的作用域:方法作用域(局部方法变量)。可以重用SqlSessionFactoryBuilder来创建多个SQLSessionFactory实例,

SqlSessionFactory:一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或创建另一个实例。它的作用域:应用作用域(application)。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession:每个线程都应该有它的SqlSession实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域是请求(request结束,即response了)或方法作用域。绝对不能将SqlSession实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将SqlSession实例的引用放在任何类型的托管作用域中,比如servlet框架中的HttpSession。如果你现在正在使用一种Web框架,要考虑SqlSession放在一个和Http请求对象相似的作用域中。换句话说,每次收到的HTTP请求,就可以打开一个SqlSession,返回一个响应,就关闭它。这个关闭操作非常重要,应该把这个关闭操作放到finally块中以确保每次都能执行关闭。一个HttpSession对应多个SQLSession。(两者作用域不同)

三、mybatis基础搭建

四、Mybatis表之间的关系(一对多,多对一)

五、动态sql

六、Mybatis的缓存(https://www.cnblogs.com/happyflyingpig/p/7739749.htm

A)一级缓存: 基于PerpetualCache 的 HashMap本地缓存其存储作用域为 Session,当 Session   flush 或 close 之后,该Session中的所有 Cache 就将清空。一级缓存默认是长开启的。(session是不共享的)

 

  1. 一级缓存的生命周期有多长?
  1. MyBatis在开启一个数据库会话时,会创建一个新的SQLSession对象,SQLSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SQLSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
  2. 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。
  3. 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。
  4. SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用
  1. 怎么判断某两次查询是完全相同的查询?

Mybatis认为,对于两次查询,如果以下条件都一样,那么就认为他们说完全相同的查询。

  1. 传入的statementId
  2. 查询时要求的结果集中的结果范围
  3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql())
  4. 传递给java.sql.Statement要设置的参数值
  5.  

新建一个新项目,练习一级缓存(d1,d2方法)

  1. 二级缓存

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

 

MyBatis的缓存机制整体设计以及二级缓存的工作模式

Namespace层面上的二级缓存默认是不开启的,二级缓存的开启需要进行配置,实现二级缓存的时候,Mybatis要求返回的POJO(java对象,bean包)必须是可序列化的。也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了<cache/>,如果我们配置了二级缓存就意味着:

  1. 映射语句文件中的所有select语句将会被缓存。
  2. 映射语句文件中的所有insert、update和delete语句(更新操作)被刷新缓存。
  3. 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
  4. 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
  5. 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  6. 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。

 

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

(1) LRU,最近最少使用的,一般最长时间不用的对象

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

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

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

 

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

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

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

开启二级缓存,使用以下几个步骤:

  1. 在config.xml中开启二级缓存:

<setting name="cacheEnabled" value="true"/>

DefaultSqlSessionFactory源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.session.defaults;

import java.sql.Connection;
import java.sql.SQLException;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }

    public SqlSession openSession(boolean autoCommit) {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);
    }

    public SqlSession openSession(ExecutorType execType) {
        return this.openSessionFromDataSource(execType, (TransactionIsolationLevel)null, false);
    }

    public SqlSession openSession(TransactionIsolationLevel level) {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), level, false);
    }

    public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
        return this.openSessionFromDataSource(execType, level, false);
    }

    public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
        return this.openSessionFromDataSource(execType, (TransactionIsolationLevel)null, autoCommit);
    }

    public SqlSession openSession(Connection connection) {
        return this.openSessionFromConnection(this.configuration.getDefaultExecutorType(), connection);
    }

    public SqlSession openSession(ExecutorType execType, Connection connection) {
        return this.openSessionFromConnection(execType, connection);
    }

    public Configuration getConfiguration() {
        return this.configuration;
    }

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }

    private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        DefaultSqlSession var8;
        try {
            boolean autoCommit;
            try {
                autoCommit = connection.getAutoCommit();
            } catch (SQLException var13) {
                autoCommit = true;
            }
             //通过configuration对象获取Mybatis相关配置信息,Environment对象包含了数据源和事务的配置

            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            Transaction tx = transactionFactory.newTransaction(connection);
            //从表面上来看,我们是用SqlSession在执行sql语句,实际呢,其实是通过excutor执行,excutor是对于Statement的封装
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var14, var14);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }

    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
        return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());
    }

    private void closeTransaction(Transaction tx) {
        if (tx != null) {
            try {
                tx.close();
            } catch (SQLException var3) {
            }
        }

    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值