精华内容
下载资源
问答
  • 像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能 Mybatis 中缓存分为级缓存,二级缓存 Mybatis 级缓存 Mybatis 二级缓存 ...

    像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能

    Mybatis 中缓存分为一级缓存,二级缓存
    在这里插入图片描述

    Mybatis 一级缓存

    一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。

    证明一级缓存的存在

    新建数据库映射实体类 User.java

    package cn.lemon.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class User implements Serializable {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    

    新建持久层接口 IUserDao.java

    package cn.lemon.dao;
    
    import cn.lemon.domain.User;
    
    public interface IUserDao {
        User findById(Integer userId);
    }
    

    新建配置文件 SqlMapConfig.xml 和 jdbc.properties

    <?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>
        <properties resource="jdbc.properties"/>
        <typeAliases>
            <package name="cn.lemon.domain"/>
        </typeAliases>
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"></transactionManager>
                <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>
            <package name="cn.lemon.dao"/>
        </mappers>
    </configuration>
    
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql:///db_mybatis
    jdbc.username=root
    jdbc.password=lemon
    

    新建接口映射文件 IUserDao.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="cn.lemon.dao.IUserDao">
        <select id="findById" parameterType="int" resultType="user">
            select * from user where id = #{id}
        </select>
    </mapper>
    

    测试类

    package cn.lemon.dao;
    
    import cn.lemon.domain.User;
    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.junit.Test;
    
    import java.io.InputStream;
    
    public class IUserDaoTest {
        private InputStream inputStream;
        private SqlSessionFactory sqlSessionFactory;
        private SqlSession sqlSession;
        private IUserDao iUserDao;
    
        @Test
        public void findById() throws Exception{
            inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
    
            /*第一次查询*/
            User user1 = iUserDao.findById(58);
            System.out.println("第一次查询:" + user1);
            System.out.println("-------------------------------------------");
    
            /*第二次查询*/
            User user2 = iUserDao.findById(58);
            System.out.println("第二次查询:" + user2);
    
            sqlSession.close();
            inputStream.close();
        }
    }
    

    运行结果:
    在这里插入图片描述
    关闭缓存,或者使用sqlSession.clearCache 清除缓存,或者对查询的这条语句执行修改,就会出现两次查询
    在这里插入图片描述

    Mybatis 二级缓存 (重要)

    二级缓存的特点

    • 二级缓存是 mapper 映射级别的缓存
    • 多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句
    • 多个SqlSession 可以共用二级缓存
    • 二级缓存是跨 SqlSession 的。
      在这里插入图片描述

    二级缓存的配置

    第一步:在 SqlMapConfig.xml 文件开启二级缓存

        <settings>
            <!-- 
                开启二级缓存的支持
                因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为 false 代表不开启二级缓存
             -->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    

    第二步:在 IUserDao.xml 中,配置相关的 Mapper 映射文件

        <!--
            开启二级缓存的支持
            <cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace
        -->
        <cache></cache>
    

    第三步:在 IUserDao.xml 中,配置 statement 上面的 useCache 属性
    在这里插入图片描述
    第四步:测试类

    package cn.lemon.dao;
    
    import cn.lemon.domain.User;
    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.junit.Test;
    
    import java.io.InputStream;
    
    public class IUserDaoTest {
    
        @Test
        public void findById() throws Exception {
            InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            /*第一次查询*/
            SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
            IUserDao iUserDao1 = sqlSession1.getMapper(IUserDao.class);
            User user1 = iUserDao1.findById(58);
            System.out.println("第一次查询:" + user1);
            System.out.println("-------------------------------------------");
            sqlSession1.close();//关闭资源
    
            /*第二次查询*/
            SqlSession sqlSession2 = sqlSessionFactory.openSession(true);//重新开启
            IUserDao iUserDao2 = sqlSession2.getMapper(IUserDao.class);
            User user2 = iUserDao2.findById(58);
            System.out.println("第二次查询:" + user2);
            System.out.println("-------------------------------------------");
            sqlSession2.close();
    
            /*第三次查询*/
            SqlSession sqlSession3 = sqlSessionFactory.openSession(true);//重新开启
            IUserDao iUserDao3 = sqlSession3.getMapper(IUserDao.class);
            User user3 = iUserDao3.findById(58);
            System.out.println("第三次查询:" + user3);
            System.out.println("-------------------------------------------");
            sqlSession3.close();
    
            inputStream.close();
        }
    }
    

    运行结果:
    在这里插入图片描述
    特别提醒:

    • 二级缓存要实现序列化接口,也就是 implements Serializable
    • 二级缓存要在同一个命名空间下,不用的命名空间有不同的缓存,如上面的例子 namespace="cn.lemon.dao.IUserDao"
    展开全文
  • mybatis一级二级缓存

    2019-01-30 14:52:35
    mybatis一级二级缓存 文章目录mybatis一级二级缓存前言一、mybatis一级缓存二、mybatis二级缓存 前言 缓存在我们的日常开发中经常遇到,尤其在我们操作数据库时,要打开连接,操作数据库,操作完毕后要关闭,...

    mybatis一级二级缓存


    前言

    缓存在我们的日常开发中经常遇到,尤其在我们操作数据库时,要打开连接,操作数据库,操作完毕后要关闭,整个流程耗时,消耗性能。但是有了缓存机制,我们可以在打开连接之前就判断,是否有缓存,有缓存,则直接从缓存中读取,这样效率提升了数倍。
    mybatis缓存分为一级缓存和二级缓存,可以配置相关的设置。
    mybatis的一级缓存是相对于SqlSession定义,二级缓存相对于mapper来定义的。

    一、mybatis一级缓存

    mybatis默认是支持一级缓存的,无需配置。
    这里我们测试一下,下面是我的测试代码

       @Test
        public void testOne() {
            SqlSession mSession = null;
            ImsUser imsUser = null;
            ImsUser imsUser2 = null;
            try {
                mSession = SqlSessionUtils.getSqlSession(sqlSessionFactory);
                ImsUserMapper mapper = mSession.getMapper(ImsUserMapper.class);
                imsUser = mapper.findById("1");
                imsUser2 = mapper.findById("1");
                mSession.commit();
            } catch (Exception ex) {
                mSession.rollback();
            } finally {
                mSession.close();
            }
            if (imsUser != null) {
                System.out.println("user1");
                System.out.println(imsUser.toString());
            }
            if (imsUser2 != null) {
                System.out.println("user2");
                System.out.println(imsUser2.toString());
            }
        }
    

    然后运行,可看到日志

    [2019-01-30 14:19:27] [main] [DEBUG] [com.fm.mapper.ImsUserMapper:62] -- Cache Hit Ratio [com.fm.mapper.ImsUserMapper]: 0.0
    [2019-01-30 14:19:27] [main] [DEBUG] [com.fm.mapper.ImsUserMapper.findById:159] -- ==>  Preparing: SELECT * FROM todo_user WHERE id=? 
    [2019-01-30 14:19:27] [main] [DEBUG] [com.fm.mapper.ImsUserMapper.findById:159] -- ==> Parameters: 1(String)
    [2019-01-30 14:19:27] [main] [DEBUG] [com.fm.mapper.ImsUserMapper.findById:159] -- <==      Total: 1
    [2019-01-30 14:19:27] [main] [DEBUG] [com.fm.mapper.ImsUserMapper:62] -- Cache Hit Ratio [com.fm.mapper.ImsUserMapper]: 0.0
    user1
    ImsUser(id=1, name=方明, phone=13825123792, password=null, idcard=null, mail=null, createtime=Mon Aug 06 10:22:55 CST 2018)
    user2
    ImsUser(id=1, name=方明, phone=13825123792, password=null, idcard=null, mail=null, createtime=Mon Aug 06 10:22:55 CST 2018)
    

    通过日志我们可以看到,我们的查询语句只执行了一次。如果不知道怎么设置mybatis的日志,可查看我之前的博文springmvc、springboot使用logback查看myabtis日志

    二、mybatis二级缓存

    我们使用同一个SqlSessionFactory,声明两个SqlSession来测试一下。
    1、在mybatis-config.xml的setting中配置cacheEnabled为ture,旧版本为false,新版本默认是true。

    <?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>
        <settings>
            <!-- 打印查询语句 -->
            <setting name="logImpl" value="SLF4J" />
            <!--开启全局二级缓存配置,true为开启,false为不开启,默认是true-->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    </configuration>
    

    2、在mapper文件中加上

    <?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.fm.mapper.ImsUserMapper">
        <cache/>
    
        <!-- 通用查询映射结果 -->
        <resultMap id="BaseResultMap" type="com.fm.entity.ImsUser">
            <id column="id" property="id" />
            <result column="name" property="name" />
            <result column="phone" property="phone" />
            <result column="password" property="password" />
            <result column="password" property="password" />
            <result column="idcard" property="idcard" />
            <result column="mail" property="mail" />
            <result column="createtime" property="createtime" />
        </resultMap>
    
        <select id="findById" resultType="com.fm.entity.ImsUser">
            SELECT * FROM todo_user WHERE id=#{userid}
        </select>
    
    </mapper>
    

    3、测试一下,代码如下:

        @Test
        public void testTwo() {
            SqlSession mSession1 = null;
            SqlSession mSession2 = null;
            ImsUser imsUser = null;
            ImsUser imsUser2 = null;
            mSession1 = SqlSessionUtils.getSqlSession(sqlSessionFactory);
            ImsUserMapper mapper = mSession1.getMapper(ImsUserMapper.class);
            imsUser = mapper.findById("1");
            mSession1.commit();
            mSession1.close();
            if (imsUser != null) {
                System.out.println("user1");
                System.out.println(imsUser.toString());
            }
    
            mSession2 = SqlSessionUtils.getSqlSession(sqlSessionFactory);
            ImsUserMapper mapper2 = mSession2.getMapper(ImsUserMapper.class);
            imsUser2 = mapper2.findById("1");
            mSession2.commit();
            mSession2.close();
            if (imsUser2 != null) {
                System.out.println("user2");
                System.out.println(imsUser2.toString());
            }
        }
    

    运行代码,查看日志

    [2019-01-30 14:47:54] [main] [DEBUG] [com.fm.mapper.ImsUserMapper:62] -- Cache Hit Ratio [com.fm.mapper.ImsUserMapper]: 0.0
    [2019-01-30 14:47:55] [main] [DEBUG] [com.fm.mapper.ImsUserMapper.findById:159] -- ==>  Preparing: SELECT * FROM todo_user WHERE id=? 
    [2019-01-30 14:47:55] [main] [DEBUG] [com.fm.mapper.ImsUserMapper.findById:159] -- ==> Parameters: 1(String)
    [2019-01-30 14:47:55] [main] [DEBUG] [com.fm.mapper.ImsUserMapper.findById:159] -- <==      Total: 1
    user1
    ImsUser(id=1, name=方明, phone=13825123792, password=null, idcard=null, mail=null, createtime=Mon Aug 06 10:22:55 CST 2018)
    [2019-01-30 14:47:55] [main] [DEBUG] [com.fm.mapper.ImsUserMapper:62] -- Cache Hit Ratio [com.fm.mapper.ImsUserMapper]: 0.5
    user2
    ImsUser(id=1, name=方明, phone=13825123792, password=null, idcard=null, mail=null, createtime=Mon Aug 06 10:22:55 CST 2018)
    

    从日志可以看到,sql语句只执行了一次,第二次直接从缓存中获取的数据。

    这里只做了简单的测试,关于myabtis的缓存,接下来我会再写一篇博文,进行深入解析。感谢关注。

    展开全文
  • MyBatis一级缓存和二级缓存

    万次阅读 多人点赞 2018-09-05 22:44:30
    MyBatis自带的缓存有级缓存和二级缓存 级缓存 Mybatis级缓存是指Session缓存。级缓存的作用域默认是个SqlSession。Mybatis默认开启级缓存。 也就是在同个SqlSession中,执行相同的查询SQL,第次...

    MyBatis自带的缓存有一级缓存和二级缓存

    一级缓存

    Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。
    也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;
    第二次以后是直接去缓存中取。
    当执行SQL查询中间发生了增删改的操作,MyBatis会把SqlSession的缓存清空。

    一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION,如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除。
    如果需要更改一级缓存的范围,可以在Mybatis的配置文件中,在下通过localCacheScope指定。

    1

    <setting name="localCacheScope" value="STATEMENT"/>

    建议不需要修改

    需要注意的是
    当Mybatis整合Spring后,直接通过Spring注入Mapper的形式,如果不是在同一个事务中每个Mapper的每次查询操作都对应一个全新的SqlSession实例,这个时候就不会有一级缓存的命中,但是在同一个事务中时共用的是同一个SqlSession。
    如有需要可以启用二级缓存。

    二级缓存

    Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。

    二级缓存是默认启用的(要生效需要对每个Mapper进行配置),如想取消,则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。

    1

    2

    3

    <settings>

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

    </settings>

    cacheEnabled默认是启用的,只有在该值为true的时候,底层使用的Executor才是支持二级缓存的CachingExecutor。具体可参考Mybatis的核心配置类org.apache.ibatis.session.Configuration的newExecutor方法实现。
    可以通过源码看看

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    ...

        public Executor newExecutor(Transaction transaction) {

            return this.newExecutor(transaction, this.defaultExecutorType);

        }

     

        public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

            executorType = executorType == null this.defaultExecutorType : executorType;

            executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

            Object executor;

            if (ExecutorType.BATCH == executorType) {

                executor = new BatchExecutor(this, transaction);

            else if (ExecutorType.REUSE == executorType) {

                executor = new ReuseExecutor(this, transaction);

            else {

                executor = new SimpleExecutor(this, transaction);

            }

     

            if (this.cacheEnabled) {//设置为true才执行的

                executor = new CachingExecutor((Executor)executor);

            }

     

            Executor executor = (Executor)this.interceptorChain.pluginAll(executor);

            return executor;

        }

    ...

    要使用二级缓存除了上面一个配置外,我们还需要在我们每个DAO对应的Mapper.xml文件中定义需要使用的cache

    1

    2

    3

    4

    5

    ...

    <mapper namespace="...UserMapper">

        <cache/><!-- 加上该句即可,使用默认配置、还有另外一种方式,在后面写出 -->

        ...

    </mapper>

    具体可以看org.apache.ibatis.executor.CachingExecutor类的以下实现
    其中使用的cache就是我们在对应的Mapper.xml中定义的cache。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

        BoundSql boundSql = ms.getBoundSql(parameterObject);

        CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);

        return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

    }

     

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

        Cache cache = ms.getCache();

        if (cache != null) {//第一个条件 定义需要使用的cache 

            this.flushCacheIfRequired(ms);

            if (ms.isUseCache() && resultHandler == null) {//第二个条件 需要当前的查询语句是配置了使用cache的,即下面源码的useCache()是返回true的  默认是true

                this.ensureNoOutParams(ms, parameterObject, boundSql);

                List<E> list = (List)this.tcm.getObject(cache, key);

                if (list == null) {

                    list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

                    this.tcm.putObject(cache, key, list);

                }

     

                return list;

            }

        }

     

        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

    }

    还有一个条件就是需要当前的查询语句是配置了使用cache的,即上面源码的useCache()是返回true的,默认情况下所有select语句的useCache都是true,如果我们在启用了二级缓存后,有某个查询语句是我们不想缓存的,则可以通过指定其useCache为false来达到对应的效果。
    如果我们不想该语句缓存,可使用useCache="false

    1

    2

    3

    4

    5

    6

    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" useCache="false">

            select

            <include refid="Base_Column_List"/>

            from tuser

            where id = #{id,jdbcType=VARCHAR}

        </select>

    cache定义的两种使用方式

    上面说了要想使用二级缓存,需要在每个DAO对应的Mapper.xml文件中定义其中的查询语句需要使用cache来缓存数据的。
    这有两种方式可以定义,一种是通过cache元素定义,一种是通过cache-ref元素来定义。
    需要注意的是
    对于同一个Mapper来讲,只能使用一个Cache,当同时使用了和时,定义的优先级更高(后面的代码会给出原因)。
    Mapper使用的Cache是与我们的Mapper对应的namespace绑定的,一个namespace最多只会有一个Cache与其绑定。

    cache元素定义

    使用cache元素来定义使用的Cache时,最简单的做法是直接在对应的Mapper.xml文件中指定一个空的元素(看前面的代码),这个时候Mybatis会按照默认配置创建一个Cache对象,准备的说是PerpetualCache对象,更准确的说是LruCache对象(底层用了装饰器模式)。
    具体的可看org.apache.ibatis.builder.xml.XMLMapperBuilder中的cacheElement()方法解析cache元素的逻辑。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    ...

        private void configurationElement(XNode context) {

            try {

                String namespace = context.getStringAttribute("namespace");

                if (namespace.equals("")) {

                    throw new BuilderException("Mapper's namespace cannot be empty");

                else {

                    this.builderAssistant.setCurrentNamespace(namespace);

                    this.cacheRefElement(context.evalNode("cache-ref"));

                    this.cacheElement(context.evalNode("cache"));//执行在后面

                    this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));

                    this.resultMapElements(context.evalNodes("/mapper/resultMap"));

                    this.sqlElement(context.evalNodes("/mapper/sql"));

                    this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

                }

            catch (Exception var3) {

                throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);

            }

        }

    ...

        private void cacheRefElement(XNode context) {

            if (context != null) {

                this.configuration.addCacheRef(this.builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));

                CacheRefResolver cacheRefResolver = new CacheRefResolver(this.builderAssistant, context.getStringAttribute("namespace"));

     

                try {

                    cacheRefResolver.resolveCacheRef();

                catch (IncompleteElementException var4) {

                    this.configuration.addIncompleteCacheRef(cacheRefResolver);

                }

            }

     

        }

     

        private void cacheElement(XNode context) throws Exception {

            if (context != null) {

                String type = context.getStringAttribute("type""PERPETUAL");

                Class<? extends Cache> typeClass = this.typeAliasRegistry.resolveAlias(type);

                String eviction = context.getStringAttribute("eviction""LRU");

                Class<? extends Cache> evictionClass = this.typeAliasRegistry.resolveAlias(eviction);

                Long flushInterval = context.getLongAttribute("flushInterval");

                Integer size = context.getIntAttribute("size");

                boolean readWrite = !context.getBooleanAttribute("readOnly"false).booleanValue();

                Properties props = context.getChildrenAsProperties();

                this.builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);

    //如果同时存在<cache>和<cache-ref>,这里的设置会覆盖前面的cache-ref的缓存

            }

     

        }

    空cache元素定义会生成一个采用最近最少使用算法最多只能存储1024个元素的缓存,而且是可读写的缓存,即该缓存是全局共享的,任何一个线程在拿到缓存结果后对数据的修改都将影响其它线程获取的缓存结果,因为它们是共享的,同一个对象。

    cache元素可指定如下属性,每种属性的指定都是针对都是针对底层Cache的一种装饰,采用的是装饰器的模式。

    1. blocking:默认为false,当指定为true时将采用BlockingCache进行封装,blocking,阻塞的意思,使用BlockingCache会在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,这样可以阻止并发情况下多个线程同时查询数据,详情可参考BlockingCache的源码。
      简单理解,也就是设置true时,在进行增删改之后的并发查询,只会有一条去数据库查询,而不会并发

       

    2. eviction:eviction,驱逐的意思。也就是元素驱逐算法,默认是LRU,对应的就是LruCache,其默认只保存1024个Key,超出时按照最近最少使用算法进行驱逐,详情请参考LruCache的源码。如果想使用自己的算法,则可以将该值指定为自己的驱逐算法实现类,只需要自己的类实现Mybatis的Cache接口即可。除了LRU以外,系统还提供了FIFO(先进先出,对应FifoCache)、SOFT(采用软引用存储Value,便于垃圾回收,对应SoftCache)和WEAK(采用弱引用存储Value,便于垃圾回收,对应WeakCache)这三种策略。
      这里,根据个人需求选择了,没什么要求的话,默认的LRU即可

    3. flushInterval:清空缓存的时间间隔,单位是毫秒,默认是不会清空的。当指定了该值时会再用ScheduleCache包装一次,其会在每次对缓存进行操作时判断距离最近一次清空缓存的时间是否超过了flushInterval指定的时间,如果超出了,则清空当前的缓存,详情可参考ScheduleCache的实现。

    4. readOnly:是否只读
      默认为false。当指定为false时,底层会用SerializedCache包装一次,其会在写缓存的时候将缓存对象进行序列化,然后在读缓存的时候进行反序列化,这样每次读到的都将是一个新的对象,即使你更改了读取到的结果,也不会影响原来缓存的对象,即非只读,你每次拿到这个缓存结果都可以进行修改,而不会影响原来的缓存结果;
      当指定为true时那就是每次获取的都是同一个引用,对其修改会影响后续的缓存数据获取,这种情况下是不建议对获取到的缓存结果进行更改,意为只读(不建议设置为true)。
      这是Mybatis二级缓存读写和只读的定义,可能与我们通常情况下的只读和读写意义有点不同。每次都进行序列化和反序列化无疑会影响性能,但是这样的缓存结果更安全,不会被随意更改,具体可根据实际情况进行选择。详情可参考SerializedCache的源码。

    5. size:用来指定缓存中最多保存的Key的数量。其是针对LruCache而言的,LruCache默认只存储最多1024个Key,可通过该属性来改变默认值,当然,如果你通过eviction指定了自己的驱逐算法,同时自己的实现里面也有setSize方法,那么也可以通过cache的size属性给自定义的驱逐算法里面的size赋值。

    6. type:type属性用来指定当前底层缓存实现类,默认是PerpetualCache,如果我们想使用自定义的Cache,则可以通过该属性来指定,对应的值是我们自定义的Cache的全路径名称。

    cache-ref元素定义

    cache-ref元素可以用来指定其它Mapper.xml中定义的Cache,有的时候可能我们多个不同的Mapper需要共享同一个缓存的
    是希望在MapperA中缓存的内容在MapperB中可以直接命中的,这个时候我们就可以考虑使用cache-ref,这种场景只需要保证它们的缓存的Key是一致的即可命中,二级缓存的Key是通过Executor接口的createCacheKey()方法生成的,其实现基本都是BaseExecutor,源码如下。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {

        if (this.closed) {

            throw new ExecutorException("Executor was closed.");

        else {

            CacheKey cacheKey = new CacheKey();

            cacheKey.update(ms.getId());

            cacheKey.update(rowBounds.getOffset());

            cacheKey.update(rowBounds.getLimit());

            cacheKey.update(boundSql.getSql());

            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

            TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();

     

            for(int i = 0; i < parameterMappings.size(); ++i) {

                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);

                if (parameterMapping.getMode() != ParameterMode.OUT) {

                    String propertyName = parameterMapping.getProperty();

                    Object value;

                    if (boundSql.hasAdditionalParameter(propertyName)) {

                        value = boundSql.getAdditionalParameter(propertyName);

                    else if (parameterObject == null) {

                        value = null;

                    else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

                        value = parameterObject;

                    else {

                        MetaObject metaObject = this.configuration.newMetaObject(parameterObject);

                        value = metaObject.getValue(propertyName);

                    }

     

                    cacheKey.update(value);

                }

            }

     

            return cacheKey;

        }

    }

    打个比方我想在MenuMapper.xml中的查询都使用在UserMapper.xml中定义的Cache,则可以通过cache-ref元素的namespace属性指定需要引用的Cache所在的namespace,即UserMapper.xml中的定义的namespace,假设在UserMapper.xml中定义的namespace是cn.chenhaoxiang.dao.UserMapper,则在MenuMapper.xml的cache-ref应该定义如下。

    1

    <cache-ref namespace="cn.chenhaoxiang.dao.UserMapper"/>

    这样这两个Mapper就共享同一个缓存了

    自定义cache就不介绍了。

    测试二级缓存

    查询测试

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    <br />/**

     * Created with IntelliJ IDEA.

     * User: 陈浩翔.

     * Date: 2018/1/10.

     * Time: 下午 10:15.

     * Explain:

     */

    @RunWith(SpringJUnit4ClassRunner.class)

    //配置了@ContextConfiguration注解并使用该注解的locations属性指明spring和配置文件之后

    @ContextConfiguration(locations = {"classpath:spring.xml","classpath:spring-mybatis.xml"})

    public class MyBatisTestBySpringTestFramework {

     

        //注入userService

        @Autowired

        private UserService userService;

     

        @Test

        public void testGetUserId(){

            String userId = "4e07f3963337488e81716cfdd8a0fe04";

            User user = userService.getUserById(userId);

            System.out.println(user);

     

     

            //前面说到spring和MyBatis整合

            User user2 = userService.getUserById(userId);

            System.out.println("user2:"+user2);

        }

    }

    接下来我们把Mapper中的cache元素删除,不使用二级缓存

    再运行测试

    对二级缓存进行了以下测试,获取两个不同的SqlSession(前面有说,Spring和MyBatis集成,每次都是不同的SqlSession)执行两条相同的SQL,在未指定Cache时Mybatis将查询两次数据库,在指定了Cache时Mybatis只查询了一次数据库,第二次是从缓存中拿的。

    Cache Hit Ratio 表示缓存命中率。
    开启二级缓存后,每执行一次查询,系统都会计算一次二级缓存的命中率。
    第一次查询也是先从缓存中查询,只不过缓存中一定是没有的。
    所以会再从DB中查询。由于二级缓存中不存在该数据,所以命中率为0.但第二次查询是从二级缓存中读取的,所以这一次的命中率为1/2=0.5。
    当然,若有第三次查询,则命中率为1/3=0.66
    0.5这个值可以从上面开启cache的图看出来,0.0的值未截取到~漏掉了~

    注意:
    增删改操作,无论是否进行提交sqlSession.commit(),均会清空一级、二级缓存,使查询再次从DB中select。
    说明:
    二级缓存的清空,实质上是对所查找key对应的value置为null,而非将对,即entry对象删除。
    从DB中进行select查询的条件是:缓存中根本不存在这个key或者缓存中存在该key所对应的entry对象,但value为null。

    设置增删改操作不刷新二级缓存:
    若要使某个增、删或改操作不清空二级缓存,则需要在其或或中添加属性flushCache="false",默认为true。

    二级缓存的使用原则

    1. 只能在一个命名空间下使用二级缓存
      由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。
    2. 在单表上使用二级缓存
      如果一个表与其它表有关联关系,那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰,所以就有可能出现多个namespace中的数据不一致现象。
    3. 查询多于修改时使用二级缓存
      在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。
    展开全文
  • 概述 一般提到MyBatis缓存的时候...后面介绍MyBatis二级缓存,包括二级缓存的基本配置用法,还有一些常用缓存框架和缓存数据库的结合。除此之外还会介绍二级缓存的适用场景,以及如何避免产生脏数据。 、Myba...

    概述

    一般提到MyBatis缓存的时候,都是指二级缓存。一级缓存( 也叫本地缓存〉默认会启用,并且不能控制,因此很少会提到。本文简单介绍MyBatis一级缓存,了解MyBatis一级缓存可以避免产生一些难以发现的错误。后面介绍MyBatis二级缓存,包括二级缓存的基本配置用法,还有一些常用缓存框架和缓存数据库的结合。除此之外还会介绍二级缓存的适用场景,以及如何避免产生脏数据。

    一、Mybatis一级缓存

    先来一个示例,看看Mybatis一级缓存如何起作用:

    public void testLlCache() {
        // 获取SqlSession
        SqlSession sqlSession = getSqlSession();
        SysUser userl = null;
        try {
            //获取UserMapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 调用selectByid 方法,查询id = 1 的用户
            userl = userMapper.selectByid(lL);
            //对当前获取的对象重新赋值
            userl.setUserName(" New Name ");
            //再次查询获取id 相同的用户
            SysUser user2 = userMapper.selectByid(lL);
            //虽然没有更新数据库,但是这个用户名和userl 重新赋值的名字相同
            Assert.assertEquals(" New Name ", user2.getUserName());
            // 无论如何, user 2 和userl 完全就是同一个实例
            Assert.assertEquals(userl, user2);
        } finally {
            //关闭当前的sqlSessio 口
            sqlSession.close();
            System.out.println("开启新的sqlSession ");
            //开始另一个新的session
            sqlSession = getSqlSession();
            try {
                //获取UserMapper 接口
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                //调用selectByid 方法,查询id = 1 的用户
                SysUser user2 = userMapper.selectByid(lL);
                // ;第二个session 获取的用户名仍然是admin
                Assert.assertNotEquals(" New Name ", user2.getUserName());
                //这里的us er2 和前一个session 查询的结采是两个不同的实例
                Assert.assertNotEquals(userl, user2);
                //执行7i11J 除操作
                userMapper.deleteByid(2 L);
                //获取user3
                SysUser user3 = userMapper.selectByid(lL);
                //这里的user2 和user3 是两个不同的实例
                Assert.assertNotEquals(user2, user3);
            } finally {
                //关闭sqlSession
                sqlSession.close();
            }
        }
    }

    查看日志可以知道:开启新sqlSession前的两次查询,user1查询了数据库,而第二句查询出来的user2,根本就没有查询,并且user2的值是user1设置后的值,所以user2只是use1r的引用。user1和user2是一个对象,原因是Mybatis的一级缓存。

    Mybatis的一级缓存是存在于SqlSession的生命周期中,在同一个SqlSession中查询时,如果查询的方法名称和参数完全一致,先去缓存对象(Map对象)中查看,如果有查询缓存则会使用查询缓存中的对象返回。

    如何清除一级缓存?

    1. 可以在查询mapper语句中添加属性<select id="selectById"  flushCache="true"  resultMap="userMap">sql</select>,这样再查询是总是清除缓存,每次都执行查询操作。
    2. 任何的insert、update、delete操作,都会清空一级缓存,所以上面的user3查询是新查询,由于一级缓存是默默的工作,因此要避免再使用过程中由于不了解而发生觉察不到的错误。

    二、Mybatis二级缓存(重点理解)

    MyBatis的二级不同于一级缓存只存在于SqlSession 的生命周期中,而是可以理解为存在于SqlSessionFactory的生命周期中。缓存数据在一般情况下是不相通的。只有在使用如Redis这样的缓存数据库时,才可以共享缓存。

    2.1 配置二级缓存

    在Mybatis的全局配置settings中一个参数cacheEnabled属性,是二级缓存的全局开关(总开关),默认是true,也就是默认开启的。如果将其设置为false,即使后面再怎么配置二级缓存也不生效。不过因为是默认开启,所以可以不用设置。如果设置请在mybatis-config.xml中添加如下代码:

    <settings>
        <!-- 其他配置 -->
        <setting name="cacheEnabled" value="true"/>
    </settings

    二级缓存的配置需要在Mapper.xml映射文件中,或者配置在Mapper.java接口中,和命名空间是绑定的。

    2.1.1  Mapper.xml中配置二级缓存

    保证二级缓存的全局配置开启,仅仅在xml文件中添加一个<cache/>元素即可,是不是很简单,配置参考如下:

    <mapper name="xxxx.xx.xx.xxMapper">
        <cache/>
        <!-- 其他配置 -->
    </mapper>

    默认的二级缓存会有如下效果:

    1. 映射语句中的所有select语句将会被缓存;
    2. 映射语句中的所有insert、updata、delete语句会刷新缓存;
    3. 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回(缓存收回策略);
    4. 根据时间表(如no Flush Interval,没有刷新间隔),缓存不会以任何时间顺序来刷新;
    5. 缓存会存储集合或对象共1024个引用(无论查询结果是什么类型的);
    6. 缓存会被视为read/write(可读、可写),即不会被其他调用者或线程所干扰(只当前用户可见)。

    <cache>元素的属性可以修改上面的默认配置,写法如下:(下面4个属性分别对应上面的3/4/5/6条效果)

    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

    属性解释:

    eviction(收回策略)

    • LRU(最近最少使用的):一处最长时间不会使用的对象,默认值
    • FIFO(先进先出):俺对象进入缓存的税讯来移除它们。
    • SOFT(软引用):移除基于垃圾回收期状态和软引用规则的对象。
    • WEAK(弱引用):更积极地移除基于垃圾收集器状态和软引用规则的对象。

    flushInterval(刷新间隔):单位毫秒,默认情况不设置,即没有刷新间隔,缓存尽在调用语句是刷新。

    size(引用条目):记住缓存的对象数目和运行环境的可用内存资源数目。默认1024

    readOnly(只读):可以设置为true和false。如果设置成true则会让每一个调用者返回的缓存对象都是同一个实例,因此不能修改(修改对象的值,影响别人),可是带来的好处是性能的提升。如果设置成false(可读可写)会通过序列化返回缓存对象的拷贝,相对会慢一些,但是安全,因此默认是false的

    2.1.2  Mapper接口中配置二级缓存

    当只通过注解方式配置二级缓存时,需要添加如下配置:

    @CacheNamespace(eviction=FifoCache.class, flushInterval=60000, size=512, readWrite=true)
    public interface RoleMapper {
        // 接口方法
    }

    注意:同时使用注解方式和XML配置文件时,如果同时配置了上述的二级缓存,就会出现异常,因为两者的命名空间相同。

    解决:使用参照缓存。

    • 如果XML中配置了二级缓存,则Mapper接口中使用@CacheNamespaceRef(RoleMapper.class)替换@CacheNamespace注解即可。
    • 如果Mapper接口中配置了二级缓存,则XML中使用<cache-ref namespace="xx.xxx.xx.RoleMapper"/>替换<cache/>即可。

    其实Mybatis中很少同时使用Mapper接口配置和XML映射文件,参照缓存除了能通用其他缓存减少配置外,主要作用是解决脏读

    2.2 使用二级缓存

    配置了二级缓存后,当调用所有的select查询方法时,二级缓存就开始起作用了,如果配置的是可读可写的缓存,Mybatis是通过使用SerializedCache序列化来实现可读可写缓存类,所以查询的对象需要实现Serializable接口,如果配置的是仅可读,则Mybatis通过Map来存储缓存对象,因此读出来的都是相同实例。

    MyBatis默认提供的缓存实现是基于Map实现的内存缓存,己经可以满足基本的应用。但是当需要缓存大量的数据时,不能仅仅通过提高内存来使用MyBatis的二级缓存,还可以选择一些类似EhCache的缓存框架或Redis缓存数据库等工具来保存MyBatis 的二级缓存数据。接下来两节,我们会介绍两个常见的缓存框架。

    2.3  集成EhCache缓存

    EhCache是一个纯粹的Java进程内的缓存框架,具有快速、精干等特点。具体来说,EhCache主要的特性如下。

    • 快速。
    • 简单。
    • 多种缓存策略。
    • 缓存数据有内存和磁盘两级,无须担心容量问题。
    • 缓存数据会在虚拟机重启的过程中写入磁盘。
    • 可以通过RMI、可插入API等方式进行分布式缓存。
    • 具有缓存和缓存管理器的侦昕接口。
    • 支持多缓存管理器实例以及一个实例的多个缓存区域。

    2.3.1  添加项目依赖

    <dependency> 
        <groupid>org.mybatis.caches</groupid> 
        <artifactid>mybatis-ehcache</artifactid> 
        <version>l.0.3</version> 
    </dependency>

    2.3.2  配置EhCache

    在src/main/resources目录下添加ehcache.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="ehcache.xsd"
        updateCheck="false" monitoring="autodetect" 
        dynamicConfig="true">
        <diskStore path="D:/cache" />
        <defaultCache 
            maxElementsinMemory="3000"
            eternal="false"
            copyOnRead="true"
            copyOnWrite="true"
            timeToidleSeconds="3600"
            timeToLiveSeconds="3600"
            overflowToDisk="true"
            diskPersistent="true"/>
        </ehcache>

    有关EhCache的详细配置,参考地址:http://www.ehcache.org/ehcache.xml中的内容。

    • copyOnRead的含义是,判断从缓存中读取数据时是返回对象的引用还是复制一个对象返回。默认情况下是false,即返回数据的引用,这种情况下返回的都是相同的对象,和MyBatis默认缓存中的只读对象是相同的。如果设置为true,那就是可读写缓存,每次读取缓存时都会复制一个新的实例。
    • copyOnWrite 的含义是,判断写入缓存时是直接缓存对象的引用还是复制一个对象然后缓存,默认也是false。 如果想使用可读写缓存,就需要将这两个属性配置为true,如果使用只读缓存,可以不配置这两个属性,使用默认值false即可。

    2.3.3  修改XML中的缓存配置

    <mapper name="xxxx.xx.xx.xxMapper">
        <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <!-- 其他配置 -->
    </mapper>

    这样配置了cache元素的type属性为ehcache的缓存类型,再添加其他属性都不起作用了,针对缓存的配置都已经在ehcache.xml中进行。因为ehcache.xml配置文件中只能配置一个默认缓存配置(<defaultCache>),如果想对单独的命名空间的Mapper做缓存配置可以使用<cache>元素,做细颗粒配置,如下:

    <cache
        name="tk.mybatis.simple.mapper.RoleMapper"
        maxElementsinMemory="3000"
        eternal="false"
        copyOnRead="true"
        copyOnWrite="true" 
        timeToidleSeconds="3600"
        timeToLiveSeconds="3600"
        overflowToDisk="true" 
        diskPersistent="true"/> 

    2.4 集成Redis缓存

    实际生产项目使用Redis做缓存的项目不常见,所以省略,想配置可参考其他资料。

    2.5 脏数据的产生和避免

    二级缓存虽然能提高应用效率,减轻数据库服务器的压力,但是如果使用不当,很容易产生脏数据。 这些脏数据会在不知不觉中影响业务逻辑, 影响应用的实效,所以我们需要了解在MyBatis缓存中脏数据是如何产生的,也要掌握避免脏数据的技巧。

    MyBatis的二级缓存是和命名空间绑定的,所以通常情况下每一个Mapper映射文件都拥有自己的二级缓存,不同Mapper的二级缓存互不影响。在常见的数据库操作中,多表联合查询非常常见,由于关系型数据库的设计, 使得很多时候需要关联多个表才能获得想要的数据。在关联多表查询时肯定会将该查询放到某个命名空间下的映射文件中,这样一个多表的查询就会缓存在该命名空间的二级缓存中。涉及这些表的增、删、改操作通常不在一个映射文件中,它们的命名空间不同, 因此当有数据变化时,多表查询的缓存未必会被清空,这种情况下就会产生脏数据

    该如何避免脏数据的出现呢?这时就需要用到参照缓存了。 当某几个表可以作为一个业务整体时,通常是让几个会关联的ER表同时使用同一个二级缓存,这样就能解决脏数据问题。在上面这个例子中,将UserMapper.xrnl 中的缓存配置修改如下。

    <mapper namespace="tk.mybatis.simple.mapper.UserMapper">
        <cache-ref namespace="tk.mybatis.simple.mapper.RoleMapper"/>
        <!-- 其他配置 -->
    </mapper>

    修改为参照缓存后,虽然这样可以解决脏数据的问题,但是并不是所有的关联查询都可以这么解决,如果有几十个表甚至所有表都以不同的关联关系存在于各自的映射文件中时,使用参照缓存显然没有意义。

    2.6 二级缓存适用场景

    二级缓存虽然好处很多,但并不是什么时候都可以使用。 在以下场景中,推荐使用二级缓存

    • 以查询为主的应用中,只有尽可能少的增、删、改操作。
    • 绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据。
    • 可以按业务划分对表进行分组时, 如关联的表比较少,可以通过参照缓存进行配置。

    除了推荐使用的情况,如果脏读对系统没有影响,也可以考虑使用。 在无法保证数据不出现脏读的情况下, 建议在业务层使用可控制的缓存代替二级缓存。

    三、总结

    通过本文的学习,我们知道了一级缓存和二级缓存的区别,学会了如何配置二级缓存,除了MyBatis 默认提供的缓存外,还学会了如何集成EhCache。另外,我们认识到了二级缓存可能带来的脏读问题,也学会了特定情况下解决脏读的办法。

    MyBatis 的二级缓存需要在特定的场景下才会适用,在选择使用二级缓存前一定要认真考虑脏读对系统的影响。在任何情况下,都可以考虑在业务层使用可控制的缓存来代替二级缓存。

    @本文内容参考《MyBatis从入门到精通》作者:刘增辉  电子工业出版社

    展开全文
  • mybatis一级缓存二级缓存 mybatis缓存概述 mybatis一级缓存 mybatis二级缓存 mybatis缓存概述CacheKey: hashcode(哈希值),checksum,namespace+id(namespace为mapper文件的命名空间,id为sql的id),.sql(查询sql),...
  • Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,也就是说一级缓存默认是开启的,一级缓存只是相对于同个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同个SqlSession对象...
  • 配套博客内容,mybatis一级缓存和二级缓存简单示例,供初学童鞋予以参考。 博客地址 https://blog.csdn.net/magi1201/article/details/85524712
  • Mybatis 级缓存和二级缓存的使用

    万次阅读 2021-01-19 10:28:52
    目录Mybatis缓存级缓存二级缓存缓存原理 Mybatis缓存 官方文档:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache MyBatis 内置了个强大的事务性查询缓存机制,它可以非常方便地配置和定制 Mybatis中...
  • MyBatis二级缓存 MySql缓存机制MyBatis一级缓存MyBatis一级缓存是sesssion会话级别的缓存,是基于当前SqlSession对象的,MyBatis一级缓存实际上是以当前sql为key对执行语句进行缓存(跟mysql缓存一样,修改任何参数的...
  • Mybatis一级缓存与二级缓存

    千次阅读 2019-01-01 15:31:16
    mybatis的有两种缓存,级缓存和二级缓存。两个缓存的不同点和相同点总结如下 不同点: 级缓存存在于一个SqlSession之内,二级缓存存在于不同的SqlSession之间 级缓存不需要手动开启,属于默认开启状态;...
  • mybatis一级缓存和二级缓存详细介绍 今天整理啦mybatis一级缓存和二级缓存下面为大家介绍一下 一·mybatis一级缓存(sqlSession级别的一个map) 1.一级缓存是一直开启的,是本地缓存,是sqlsession级别的缓存,与...
  • 级缓存是SqlSession级别的缓存,级缓存的作用域是同个...二级缓存是mapper级别的缓存,其作用域是mapper的同个namespace。Mybatis默认没有开启二级缓存。mybaits的二级缓存是mapper范围级别,除了在SqlMapConf
  • MyBatis一级缓存 一级缓存:是SqlSession级别的,也就是同一个SqlSession内执行相同select语句的时候,不再去查询数据库,而是从Mybatis...MyBatis二级缓存 开启二级缓存 <setting name="cacheEnabled" value="t
  • 【MyBatis】MyBatis一级缓存和二级缓存

    千次阅读 2018-01-27 19:35:53
    MyBatis自带的缓存有级缓存和二级缓存 级缓存Mybatis级缓存是指Session缓存。级缓存的作用域默认是个SqlSession。Mybatis默认开启级缓存。 也就是在同个SqlSession中,执行相同的查询SQL,第次会...
  • mybatis一级缓存二级缓存 一级缓存
  • 目录级缓存二级缓存   级缓存 @Autowired private SqlSessionFactory sqlSessionFactory; public void test() { SqlSession sqlSession = sqlSessionFactory.openSession(); //分页参数 RowBounds ...
  • 【MyBatis 3】MyBatis一级缓存和二级缓存.pdf
  • mybatis一级缓存 mybatis一级缓存是SqlSession级别的缓存。  一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第次...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,801
精华内容 17,920
关键字:

mybatis的一级二级缓存