mybatis3.5.1源码详解

1.环境搭建

1.1.pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.boxuegu.javaee</groupId>
    <artifactId>mybatis-source-learn</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatis-source-learn</name>
    <description>mybatis-source-learn</description>
    <properties>
        <!--  统一编译版本  -->
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--  mybatis版本  -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!-- 数据库连接  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!--  log4j日志  -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--  单元测试  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

1.2.数据库脚本:
SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for bxg_employee
-- ----------------------------
DROP TABLE IF EXISTS `bxg_employee`;
CREATE TABLE `bxg_employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `last_name` varchar(255) DEFAULT NULL,
  `gender` char(1) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of bxg_employee
-- ----------------------------
INSERT INTO `bxg_employee` VALUES ('1', 'zhangsan', '1', '[email protected]');
1.3.mybatis-config.xml:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Cofig 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--  mybatis插件机制,这里不讲(用的不多) -->
<!--    <plugins>-->
<!--        <plugin interceptor="com.boxuegu.javaee.mybatissourcelearn.MyIntercepts">-->
<!--            <property name="test" value="testvalue"/>-->
<!--        </plugin>-->
<!--    </plugins>-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="EmployeeMapper.xml"></mapper>
    </mappers>
</configuration>
1.4.EmployeeMapper:
//mapper层
public interface EmployeeMapper {
    public Employee getEmployeeById(Integer id);
}
1.5.Employee:
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private String gender;
}

2.测试接口

public class Test {
    public static void main(String[] args) {
        //1.加载配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            //1.1 获取资源转为InputStream
            //mybatis自己实现的资源解析流
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //2. 获取sqlSessionFactory,建造工厂模式的使用
        //通过使用sqlSessionFactory获取SQL会话
        SqlSessionFactory sqlSessionFactory = 
        new SqlSessionFactoryBuilder().build(inputStream);

        //3. 获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            //谈过xml文件直接执行sql语句
            //Employee employee = sqlSession.selectOne("com.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper.getEmployeeById", 1);
            //alt+shift+L introduce local variables;
            Thread thread = new Thread(() -> System.out.println("test"));
            //4. 获取mapper接口实现
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            System.out.println("mapper::::" + mapper.getClass());

            //5. 执行sql语句
            Employee employee = mapper.getEmployeeById(1);
            System.out.println(employee);
        } finally {
            sqlSession.close();
        }
    }
}

3.获取resource转为InputStream

3.1.定义一个配置文件为资源:
 //1.加载配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            //mybatis自己实现的资源解析流
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
3.2.getResourceAsStream,Ctrl+Enter进去:
 /**
     * Returns a resource on the classpath as a Stream object
     * 以流对象的形式返回类路径上的资源
     *
     * @param resource The resource to find
     * @return The resource   
     * @throws java.io.IOException If the resource cannot be found or read
     */
    public static InputStream getResourceAsStream(String resource) throws IOException {
        return getResourceAsStream(null, resource);
    }

注释说的很明显,以流对象的形式返回类路径上的资源,这个路径可以是相对路径,绝对路径,比如说文件上传就是绝对路径,参数里面的null是ClassLoader类型,这里没有加上,待会会加上类加载器;继续看,查看它return的方法,Ctrl+Enter

//大多数方法都是委托给ClassLoaderWrapper,再去做真正的事  
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
 /**
     * Returns a resource on the classpath as a Stream object
     * 以流对象的形式返回类路径上的资源
     *
     * @param loader   The classloader used to fetch the resource
     * @param resource The resource to find
     * @return The resource
     * @throws java.io.IOException If the resource cannot be found or read
     */
    public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
        InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
        if (in == null) {
            throw new IOException("Could not find resource " + resource);
        }
        return in;
    }
 /**
     * Get a resource from the classpath, starting with a specific class loader
     * 从类路径获取资源,从特定的类装入器开始
     *
     * @param resource    - the resource to find
     * @param classLoader - the first class loader to try
     * @return the stream or null
     */
    public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
        return getResourceAsStream(resource, getClassLoaders(classLoader));
    }
/**
     * 用5个类加载器一个个查找资源,只要其中任何一个找到,就返回
     * Try to get a resource from a group of classloaders
     *
     * @param resource    - the resource to get
     * @param classLoader - the classloaders to examine
     * @return the resource or null
     */
    InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
        for (ClassLoader cl : classLoader) {
            if (null != cl) {

                // try to find the resource as passed
                InputStream returnValue = cl.getResourceAsStream(resource);

                // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
                if (null == returnValue) {
                    returnValue = cl.getResourceAsStream("/" + resource);
                }

                if (null != returnValue) {
                    return returnValue;
                }
            }
        }
        return null;
    }
 /**
     * Returns an input stream for reading the specified resource.
     *  返回读取指定资源的输入流。
     * <p> The search order is described in the documentation for {@link
     * #getResource(String)}.  </p>
     *
     * @param  name
     *         The resource name
     *
     * @return  An input stream for reading the resource, or <tt>null</tt>
     *          if the resource could not be found
     *
     * @since  1.1
     */
    public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }
3.3.这里就是使用到了类加载器,

使用类加载器去加载资源,这里给一个外连接,可以细看,理论通用的.

[https://www.cnblogs.com/softidea/p/4251433.html]:

总的来说就是URL对象调用openStream()方法可以返回一个输入流,该输入流指向URL对象所包含的资源.

好,到这里资源就加载了核心配置文件并返回给inputStream.

4.构建SqlSessionFactory

        //2. 获取sqlSessionFactory,建造工厂模式的使用
        //通过使用sqlSessionFactory获取SQL会话
        SqlSessionFactory sqlSessionFactory = 
        new SqlSessionFactoryBuilder().build(inputStream);
4.1.这个SqlSessionFactoryBuilder就是一个普通的类,进去:
public class SqlSessionFactoryBuilder {
    //SqlSessionFactoryBuilder有9个build()方法

它有9个构造方法,我们只看是用到了哪一个

 //以下3个方法都是调用下面第8种方法
    public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }

另外两个对象是空的,先不管,

 /**
     * 第8种方法和第4种方法差不多,Reader换成了InputStream
     *
     * @param inputStream
     * @param environment
     * @param properties
     * @return
     */
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            //建造者模式
            //专门处理配置文件的模式
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
4.2.这里使用了建造者模式XMLConfigBuilder专门处理配置文件,
//inputstream系列的构造函数
    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        //得到XPathParser实例之后,就调用另一个使用XPathParser作为配置来源的重载构造函数了
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }

这里面有有一个XMLMapperEntityResolver(),

public class XMLMapperEntityResolver implements EntityResolver {

    // <?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">
    // 常量定义
    //dtd文件就是xml的校验文件,或者是规范文件
    private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
    private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
    private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
    private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";

    private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
    private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
    
4.3.dtd文件主要用于xml文件的校验工作,

进入this(new XPathParser(方法

this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
 /**
     * new XPathParser(reader, true, props, new XMLMapperEntityResolver())
     * 的参数含义分别是Reader,是否进行DTD 校验,属性配置,XML实体节点解析器。
     * <p>
     * entityResolver 类似于spring的XML标签解析器,
     * 有默认的解析器,也有自定义的比如tx,dubbo等,在这里mybatis硬编码为了XMLMapperEntityResolver。
     *
     * @param inputStream
     * @param validation
     * @param variables
     * @param entityResolver
     */
    public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
        commonConstructor(validation, variables, entityResolver);
        this.document = createDocument(new InputSource(inputStream));
    }
4.4.构造函数赋值
commonConstructor(validation, variables, entityResolver);
 /**
     * 普通构造函数,除了把参数都设置到实例变量里面去以外,还初始化了XPath
     * 设置字段,创建xpath
     * @param validation
     * @param variables
     * @param entityResolver
     */
    private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
        this.validation = validation;
        this.entityResolver = entityResolver;
        this.variables = variables;
        //解析xmlPath的工厂模式,定义变量
        XPathFactory factory = XPathFactory.newInstance();
        this.xpath = factory.newXPath();
    }
4.5.resource转为dom树.
 /**
     * 创建dom
     *  主要是根据mybatis自身需要创建一个文档解析器,然后调用parse将输入input source解析为DOM XML文档并返回。
     *
     * @param inputSource
     * @return
     */
    private Document createDocument(InputSource inputSource) {
        // important: this must only be called AFTER common constructor
        try {
            //这个是DOM解析方式
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(validation);

            //设置由本工厂创建的解析器是否支持XML命名空间
            factory.setNamespaceAware(false);
            //忽略注释
            factory.setIgnoringComments(true);
            //忽略空白
            factory.setIgnoringElementContentWhitespace(false);
            //设置是否将CDATA节点转换为Text节点
            factory.setCoalescing(false);
            //设置是否展开实体引用节点,这里应该是sql片段引用的关键
            factory.setExpandEntityReferences(true);

            DocumentBuilder builder = factory.newDocumentBuilder();
            //设置解析mybatis xml文档节点的解析器,也就是上面的XMLMapperEntityResolver
            //需要注意的就是定义了EntityResolver(XMLMapperEntityResolver),这样不用联网去获取DTD,
            //将DTD放在org\apache\ibatis\builder\xml\mybatis-3-config.dtd,来达到验证xml合法性的目的
            builder.setEntityResolver(entityResolver);
            builder.setErrorHandler(new ErrorHandler() {
                @Override
                public void error(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                @Override
                public void fatalError(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                @Override
                public void warning(SAXParseException exception) throws SAXException {
                }
            });
            return builder.parse(inputSource);
        } catch (Exception e) {
            throw new BuilderException("Error creating document instance.  Cause: " + e, e);
        }
    }
4.6.使用了Configuration对象封装dom树的全部节点信息.

返回Document的DOM树,再通过将DOM树转换成XML配置构建器,将XML配置构建器解析成Configuration,其实到了这里就校验了配置文件的合法性,并最终解析配置文件的节点数据交给Configuration对象,

//建造者模式
//专门处理配置文件的模式
 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
/**
     * 解析配置
     *
     * @return
     */
    public Configuration parse() {
        //首先判断有没有解析过配置文件,只有没有解析过才允许解析。如果已经解析过了,报错
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        //根节点是configuration  mybatis配置文件解析的主流程
        //其中调用了parser.evalNode(“/configuration”)返回根节点的org.apache.ibatis.parsing.XNode表示,
        // XNode里面主要把关键的节点属性和占位符变量结构化出来
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }

返回的是Configuration对象,后面SqlSessionFactory也会用到它:

/**
     * 最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
     *
     * @param config
     * @return
     */
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

这里就回到了那默认的9个构造方法,这里做了什么,进去看看,果然是默认,他自己实现了SqlSessionFactory这个接口

return new DefaultSqlSessionFactory(config);
public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

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

好,到现在SqlSessionFactory创建完毕.那么 SqlSessionFactory里面有什么呢?就是SqlSession,

5.获取SqlSession

//3. 获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
public interface SqlSessionFactory {
  //8个方法可以用来创建SqlSession实例

  SqlSession openSession();
 //最终都会调用2种方法:openSessionFromDataSource,openSessionFromConnection
    //以下6个方法都会调用openSessionFromDataSource
    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }

这个方法做了什么?

// ExecutorType 执行类型,TransactionIsolationLevel 事务级别,autoCommit 是否自动提交
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    public ExecutorType getDefaultExecutorType() {
        return defaultExecutorType;
    }
 	//默认为简单执行器 SIMPLE 就是普通的执行器;
    // REUSE 执行器会重用预处理语句(prepared statements);
    // BATCH 执行器将重用语句并执行批量更新。
    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
5.1.使用的是默认执行器,

openSessionFromDataSource()

 /**
     * @param execType
     * @param level
     * @param autoCommit 是否自动提交事务 事务管理模式分为两种,自动提交和手工提交
     * @return
     */
    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
            final Environment environment = configuration.getEnvironment();
            final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
            //通过事务工厂来产生一个事务
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            //生成一个执行器(事务包含在执行器里)
            final Executor executor = configuration.newExecutor(tx, execType);
            //然后产生一个DefaultSqlSession
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            //如果打开事务出错,则关闭它
            closeTransaction(tx); // may have fetched a connection so lets call close()
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
            //最后清空错误上下文
            ErrorContext.instance().reset();
        }
    }
5.2.最终返回的是SqlSession
public class DefaultSqlSession implements SqlSession {

    private final Configuration configuration;
    private final Executor executor;

到了这里,SqlSession已经出来了.

6.获取mapper接口

//4. 获取mapper接口实现
 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
 System.out.println("mapper::::" + mapper.getClass());
  /**
   *  得到映射器,这个巧妙的使用了泛型,使得类型安全
   *  到了MyBatis 3,还可以用注解,这样xml都不用写了
   * Retrieves a mapper.
   * @param <T> the mapper type
   * @param type Mapper interface class
   * @return a mapper bound to this SqlSession
   */
  <T> T getMapper(Class<T> type);
6.1返回了泛型,就是接口自己本身
public interface EmployeeMapper {
    public Employee getEmployeeById(Integer id);
}

7.调用接口的方法

//5. 执行sql语句
Employee employee = mapper.getEmployeeById(1);
 System.out.println(employee);
7.1.mapper找xml文件的sql
<?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.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper">
    <select id="getEmployeeById" resultType="com.boxuegu.javaee.mybatissourcelearn.dto.Employee">
        select id,last_name lastname,gender, email from bxg_employee where id=#{id}
    </select>
</mapper>
7.2.返回result:

这里一个简单的mybatis执行流程就完成了.

mapper::::class com.sun.proxy.$Proxy0
Employee{id=1, lastName='zhangsan', email='[email protected]', gender='1'}

8.一级缓存和二级缓存

8.1.它们之间的区别
一级缓存:一级缓存是SqlSession级别,属于单个会话级别的,默认开启,第一次是查询数据库,第二次从缓存中找数据,怎么判断两次会话是否相同?
传入的statementId,查询时要求的结果集中的结果范围,传递给java.sql.Statement要设置的参数值.

二级缓存(全局缓存),

属于SqlSessionFactory级别,mybatis是默认不开启的,需要手动开启,
 <!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
        <setting name="cacheEnabled" value="true" />
实现二级缓存的pojo必须序列化(实现Serializable接口),实现序列化的数据有多种介质可以存储,不是单独的内存.

开启二级缓存效果:

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

mybatis-config.xml配置,一定要注意最好写在的下面.不然会报错

8.2.二级缓存实现
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Cofig 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--  二级缓存实现  -->
    <settings>
        <setting name="cacheEnabled" value="true"/>  <!-- 全局映射器启用缓存 -->
        <setting name="useGeneratedKeys" value="true"/>  <!-- 允许 JDBC 支持自动生成主键 -->
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="EmployeeMapper.xml"></mapper>
    </mappers>
</configuration>

EmployeeMapper.xml配置,数据库有4条数据,期望查询3条集合,但只显示一条sql,证明只查询了一次数据库,每一条查询结束就关闭session会话:

<?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.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper">
    <!--开启本mapper的namespace下的二级缓存-->
    <!--
        eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
        (1) LRU,最近最少使用的,一处最长时间不用的对象
        (2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
        (3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
        (4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
                移除最长时间不用的对形象

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

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

        readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
        办法修改缓存,他的默认值是false,不允许我们修改
    -->
    <cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
    <select id="getEmployeeById" resultType="com.boxuegu.javaee.mybatissourcelearn.dto.Employee">
        select id,last_name lastname,gender, email from bxg_employee where id=#{id}
    </select>
    <select id="getEmployeeList" resultType="com.boxuegu.javaee.mybatissourcelearn.dto.Employee">
        select id,last_name lastname,gender, email from bxg_employee
    </select>
</mapper>

示例:

package com.boxuegu.javaee.mybatissourcelearn;

import com.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper;
import com.boxuegu.javaee.mybatissourcelearn.dto.Employee;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @Description
 * @Author MissYu
 * @Date 2020/6/18 22:22
 * @Classname test2
 */
public class test2 {

    public static void main(String[] args) {
        //1.加载配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            //mybatis自己实现的资源解析流
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //2. 获取sqlSessionFactory,建造工厂模式的使用
        //通过使用sqlSessionFactory获取SQL会话
        SqlSessionFactory sqlSessionFactory = 
         new SqlSessionFactoryBuilder().build(inputStream);

        //3. 获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            //4. 获取mapper接口实现
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);

            //5. 执行sql语句
            List<Employee> employeeList = mapper.getEmployeeList();
            System.err.println("第一次執行:    :" + employeeList);
            //每次都关闭sqlsession,避免让mybatis以为是一级缓存.
            sqlSession.commit();

            List<Employee> employeeList2 = mapper.getEmployeeList();
            System.err.println("第二次執行:    :" + employeeList2);
            //每次都关闭sqlsession,避免让mybatis以为是一级缓存.
            sqlSession.commit();

            List<Employee> employeeList3 = mapper.getEmployeeList();
            System.err.println("第三次執行:    :" + employeeList3);
            //每次都关闭sqlsession,避免让mybatis以为是一级缓存.
            sqlSession.commit();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            sqlSession.close();
        }

    }
}

结果:

​ 下面三个结果集,只显示了一次sql,这句sql只出现了1次,这样就已经达到了我们的效果了.

Preparing: select id,last_name lastname,gender, email from bxg_employee 
[22:57:59:968] [DEBUG] - org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:73) - Cache Hit Ratio [com.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper]: 0.0
[22:58:00:223] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:142) - ==>  Preparing: select id,last_name lastname,gender, email from bxg_employee 
[22:58:00:261] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:142) - ==> Parameters: 
[22:58:00:279] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:142) - <==      Total: 4
[22:58:00:280] [DEBUG] - org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:73) - Cache Hit Ratio [com.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper]: 0.5
[22:58:00:281] [DEBUG] - org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:73) - Cache Hit Ratio [com.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper]: 0.6666666666666666
第一次執行:    :[Employee{id=1, lastName='zhangsan', email='[email protected]', gender='1'}, Employee{id=2, lastName='杜蕾斯', email='fasdfsd@qq,com', gender='4'}, Employee{id=3, lastName='馬克龍', email='[email protected]', gender='0'}, Employee{id=4, lastName='我是誰', email='[email protected]', gender='8'}]
第二次執行:    :[Employee{id=1, lastName='zhangsan', email='[email protected]', gender='1'}, Employee{id=2, lastName='杜蕾斯', email='fasdfsd@qq,com', gender='4'}, Employee{id=3, lastName='馬克龍', email='[email protected]', gender='0'}, Employee{id=4, lastName='我是誰', email='[email protected]', gender='8'}]
第三次執行:    :[Employee{id=1, lastName='zhangsan', email='[email protected]', gender='1'}, Employee{id=2, lastName='杜蕾斯', email='fasdfsd@qq,com', gender='4'}, Employee{id=3, lastName='馬克龍', email='[email protected]', gender='0'}, Employee{id=4, lastName='我是誰', email='[email protected]', gender='8'}]

另外关于一级缓存和二级缓存共存,你开启了二级,如果一级缓存有数据就拿一级缓存的,没有拿二级缓存的.至此.mybatis全部结束.

猜你喜欢

转载自blog.csdn.net/weixin_45029766/article/details/106845481