精华内容
下载资源
问答
  • Mybatis缓存

    千次阅读 多人点赞 2021-02-16 15:39:15
    Mybatis缓存缓存的基本概念Mybatis一级缓存证明一级缓存的存在编写用户持久层 Dao 接口编写用户持久层映射文件编写测试方法一级缓存的分析测试一级缓存的清空添加updateUser方法测试缓存的同步Mybatis二级缓存二级...

    缓存的基本概念

    缓存是存在于内存中的临时数据。
    使用缓存减少和数据库的交互次数,提高执行效率。
    适用于缓存:
    (1)经常查询并且不经常改变的。
    (2)数据的正确与否对最终结果影响不大的。
    不适用于缓存:
    (1)经常改变的数据
    (2)数据的正确与否对最终结果影响很大的。
    例如:商品的库存,银行的汇率,股市的牌价。

    Mybatis一级缓存

    一级缓存:
    它指的是Mybatis中SqlSession对象的缓存。
    当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
    该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
    查询是否有,有的话直接拿出来用。
    当SqlSession对象消失时,mybatis的一级缓存也就消失了。

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

    证明一级缓存的存在

    编写用户持久层 Dao 接口

    IUserDao:

    package com.keafmd.dao;
    import com.keafmd.domain.User;
    import java.util.List;
    
    /**
     * Keafmd
     *
     * @ClassName: IUserDao
     * @Description: 用户的持久层接口
     * @author: 牛哄哄的柯南
     * @date: 2021-02-06 19:29
     */
    
    public interface IUserDao {
        /**
         * 查询所有用户,同时获取到用户下所有账户的信息
         * @return
         */
        List<User> findAll();
    
    
        /**
         * 根据id查新用户信息
         * @param id
         * @return
         */
        User findById(Integer id);
    
    }
    

    编写用户持久层映射文件

    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="com.keafmd.dao.IUserDao">
    
    
        <!--配置查询所有-->
        <select id="findAll" resultType="user">
            select * from user
        </select>
    
        <!--根据id查询用户-->
        <select id="findById" parameterType="Integer" resultType="user">
            select * from user where id = #{id}
        </select>
    
    </mapper>
    

    编写测试方法

    MybatisTest:

    package com.keafmd.test;
    
    import com.keafmd.dao.IUserDao;
    import com.keafmd.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.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.InputStream;
    import java.util.List;
    
    /**
     * Keafmd
     *
     * @ClassName: MybatisTest
     * @Description: 测试类,测试crud操作
     * @author: 牛哄哄的柯南
     * @date: 2021-02-08 15:24
     */
    public class UserTest {
    
        private InputStream in;
        private SqlSessionFactory factory;
        private SqlSession sqlSession;
        private IUserDao userDao;
    
        @Before // 用于在测试方法执行前执行
        public void init()throws Exception{
            //1.读取配置文件,生成字节输入流
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建SqlSessionFactory工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            factory = builder.build(in);
            //3.使用工厂生产SqlSession对象
            sqlSession = factory.openSession(true); //里面写个true,下面每次就不用了写 sqlsession.commit(); 了
            //4.使用SqlSession创建Dao接口的代理对象
            userDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After // 用于在测试方法执行后执行
        public void destory() throws Exception{
            //提交事务
            //sqlSession.commit();
            //6.释放资源
            sqlSession.close();
            in.close();
        }
    
    
        /**
         * 测试一级缓存
         */
        @Test
        public void testFirstLevelCache(){
            User user1 = userDao.findById(41);
            System.out.println(user1);
            User user2 = userDao.findById(41);
            System.out.println(user2);
            System.out.println(user1==user2);
        }
    
    }
    

    运行结果:

    2021-02-16 15:52:44,863 359    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2021-02-16 15:52:45,164 660    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 905735620.
    2021-02-16 15:52:45,168 664    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ?
    2021-02-16 15:52:45,200 696    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
    2021-02-16 15:52:45,227 723    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - <==      Total: 1
    com.keafmd.domain.User@400cff1a
    com.keafmd.domain.User@400cff1a
    true
    

    我们可以看到,只发起了一次查询,相当于第一次是查询,而第二次是从缓存中取得。

    一级缓存的分析

    一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

    举个例子:
    在这里插入图片描述
    第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。
    得到用户信息,将用户信息存储到一级缓存中。
    如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
    第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。

    测试一级缓存的清空

    改变下测试代码:

    /**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);
    
        /*sqlSession.close();
    
        //再次获取sqlSession对象
        sqlSession = factory.openSession();*/
    
        sqlSession.clearCache();//此方法也可以清空缓存
    
        userDao = sqlSession.getMapper(IUserDao.class);
    
        User user2 = userDao.findById(41);
        System.out.println(user2);
    
        System.out.println(user1==user2);
    }
    

    运行结果:

    2021-02-16 16:14:57,171 194    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2021-02-16 16:14:57,498 521    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 93314457.
    2021-02-16 16:14:57,503 526    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ?
    2021-02-16 16:14:57,541 564    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
    2021-02-16 16:14:57,560 583    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - <==      Total: 1
    com.keafmd.domain.User@57175e74
    2021-02-16 16:14:57,561 584    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ?
    2021-02-16 16:14:57,561 584    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
    2021-02-16 16:14:57,562 585    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - <==      Total: 1
    com.keafmd.domain.User@c540f5a
    false
    2021-02-16 16:14:57,563 586    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@58fdd99]
    2021-02-16 16:14:57,563 586    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 93314457 to pool.
    
    Process finished with exit code 0
    

    我们可以看到这样就发起了两次查询,我们用这两种方式都可以清空缓存。

    添加updateUser方法

    IUserDao:

    package com.keafmd.dao;
    import com.keafmd.domain.User;
    import java.util.List;
    
    /**
     * Keafmd
     *
     * @ClassName: IUserDao
     * @Description: 用户的持久层接口
     * @author: 牛哄哄的柯南
     * @date: 2021-02-06 19:29
     */
    
    public interface IUserDao {
        /**
         * 查询所有用户,同时获取到用户下所有账户的信息
         * @return
         */
        List<User> findAll();
    
    
        /**
         * 根据id查新用户信息
         * @param id
         * @return
         */
        User findById(Integer id);
    
        /**
         * 更新用户信息
         * @param user
         */
        void updateUser(User user);
    
    
    }
    

    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="com.keafmd.dao.IUserDao">
    
        <!--配置查询所有-->
        <select id="findAll" resultType="user">
            select * from user
        </select>
    
        <!--根据id查询用户-->
        <select id="findById" parameterType="Integer" resultType="user">
            select * from user where id = #{id}
        </select>
    
        <!--更新用户信息-->
        <select id="updateUser"  resultType="user">
            update user set username=#{username},address=#{address} where id=#{id}
        </select>
    
    </mapper>
    

    测试缓存的同步

    UserTest:

    package com.keafmd.test;
    
    import com.keafmd.dao.IUserDao;
    import com.keafmd.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.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.InputStream;
    import java.util.List;
    
    /**
     * Keafmd
     *
     * @ClassName: MybatisTest
     * @Description: 测试类
     * @author: 牛哄哄的柯南
     * @date: 2021-02-08 15:24
     */
    public class UserTest {
    
        private InputStream in;
        private SqlSessionFactory factory;
        private SqlSession sqlSession;
        private IUserDao userDao;
    
        @Before // 用于在测试方法执行前执行
        public void init()throws Exception{
            //1.读取配置文件,生成字节输入流
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建SqlSessionFactory工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            factory = builder.build(in);
            //3.使用工厂生产SqlSession对象
            sqlSession = factory.openSession(true); //里面写个true,下面每次就不用了写 sqlsession.commit(); 了
            //4.使用SqlSession创建Dao接口的代理对象
            userDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After // 用于在测试方法执行后执行
        public void destory() throws Exception{
            //提交事务
            //sqlSession.commit();
            //6.释放资源
            sqlSession.close();
            in.close();
        }
    
    
        /**
         * 测试一级缓存
         */
        @Test
        public void testFirstLevelCache(){
            User user1 = userDao.findById(41);
            System.out.println(user1);
    
            /*sqlSession.close();
    
            //再次获取sqlSession对象
            sqlSession = factory.openSession();*/
    
            sqlSession.clearCache();//此方法也可以清空缓存
    
            userDao = sqlSession.getMapper(IUserDao.class);
    
            User user2 = userDao.findById(41);
            System.out.println(user2);
    
            System.out.println(user1==user2);
        }
    
        /**
         * 测试缓存的同步
         */
        @Test
        public void testClearCache(){
            //1.根据id查询用户
            User user1 = userDao.findById(41);
            System.out.println(user1);
    
            //2.更新用户信息
            user1.setUsername("update user clear cache");
            user1.setAddress("上海");
            userDao.updateUser(user1);
    
            //手动提交
            sqlSession.commit();
    
            //3.再次查询id为41的用户
            User user2 = userDao.findById(41);
            System.out.println(user2);
    
            System.out.println(user1==user2);
        }
    
    
    }
    

    运行testClearCache():

    2021-02-16 16:13:04,277 209    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2021-02-16 16:13:04,560 492    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 93314457.
    2021-02-16 16:13:04,565 497    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ?
    2021-02-16 16:13:04,618 550    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
    2021-02-16 16:13:04,645 577    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - <==      Total: 1
    com.keafmd.domain.User@57175e74
    2021-02-16 16:13:04,646 578    [           main] DEBUG keafmd.dao.IUserDao.updateUser  - ==>  Preparing: update user set username=?,address=? where id=?
    2021-02-16 16:13:04,646 578    [           main] DEBUG keafmd.dao.IUserDao.updateUser  - ==> Parameters: update user clear cache(String), 上海(String), 41(Integer)
    2021-02-16 16:13:04,647 579    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ?
    2021-02-16 16:13:04,648 580    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
    2021-02-16 16:13:04,649 581    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - <==      Total: 1
    com.keafmd.domain.User@c540f5a
    false
    2021-02-16 16:13:04,650 582    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@58fdd99]
    2021-02-16 16:13:04,650 582    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 93314457 to pool.
    
    Process finished with exit code 0
    

    我们在第一次查询后进行的更新操作,所以会清空一级缓存,然后第二次会在进行查询。

    Mybatis二级缓存

    二级缓存:
    它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
    二级缓存的使用步骤:
    第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
    第三步:让当前的操作支持二级缓存(在select标签中配置)
    二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

    二级缓存结构图

    在这里插入图片描述
    首先开启 mybatis 的二级缓存。
    sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
    如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交,将会清空该 mapper 映射下的二级缓存区域的数据。
    sqlSession2 去查询与 sqlSession1 相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

    二级缓存的开启与关闭

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

    <settings>
        <!--开启二级缓存的支持-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    

    因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存。

    第二步:配置相关的 Mapper 映射文件

    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="com.keafmd.dao.IUserDao">
    
    
        <!--开启user支持二级缓存-->
        <cache/>
    
        <!--配置查询所有-->
        <select id="findAll" resultType="user">
            select * from user
    
        </select>
    
        <!--根据id查询用户-->
        <select id="findById" parameterType="Integer" resultType="user" useCache="true">
            select * from user where id = #{id}
        </select>
    
        <!--更新用户信息-->
        <select id="updateUser"  resultType="user">
            update user set username=#{username},address=#{address} where id=#{id}
        </select>
    
    
    </mapper>
    

    第三步:配置 statement 上面的 useCache 属性

    <!--根据id查询用户-->
    <select id="findById" parameterType="Integer" resultType="user" useCache="true">
        select * from user where id = #{id}
    </select>
    

    将 UserDao.xml 映射文件中的标签中设置 useCache=”true”代表当前这个statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。
    注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

    二级缓存测试

    SecondLevelCacheTest:

    package com.keafmd.test;
    
    import com.keafmd.dao.IUserDao;
    import com.keafmd.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.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.InputStream;
    
    /**
     * Keafmd
     *
     * @ClassName: MybatisTest
     * @Description: 测试类,测试crud操作
     * @author: 牛哄哄的柯南
     * @date: 2021-02-08 15:24
     */
    public class SecondLevelCacheTest {
    
        private InputStream in;
        private SqlSessionFactory factory;
    
    
        @Before // 用于在测试方法执行前执行
        public void init()throws Exception{
            //1.读取配置文件,生成字节输入流
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建SqlSessionFactory工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            factory = builder.build(in);
    
        }
    
        @After // 用于在测试方法执行后执行
        public void destory() throws Exception{
    
            in.close();
        }
    
    
        /**
         * 测试二级缓存
         */
        @Test
        public void testSecondLevelCache(){
            SqlSession sqlSession1 = factory.openSession();
            IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
            User user1 = dao1.findById(41);
            System.out.println(user1);
            sqlSession1.close(); //一级缓存消失
    
            SqlSession sqlSession2 = factory.openSession();
            IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
            User user2 = dao2.findById(41);
            System.out.println(user2);
            sqlSession2.close();
    
            System.out.println(user1==user2);
        }
    
    }
    

    运行结果:

    2021-02-16 17:07:05,474 179    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2021-02-16 17:07:05,723 428    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 905735620.
    2021-02-16 17:07:05,723 428    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@35fc6dc4]
    2021-02-16 17:07:05,726 431    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ?
    2021-02-16 17:07:05,752 457    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - ==> Parameters: 41(Integer)
    2021-02-16 17:07:05,770 475    [           main] DEBUG m.keafmd.dao.IUserDao.findById  - <==      Total: 1
    com.keafmd.domain.User@400cff1a
    2021-02-16 17:07:05,777 482    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@35fc6dc4]
    2021-02-16 17:07:05,778 483    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@35fc6dc4]
    2021-02-16 17:07:05,778 483    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 905735620 to pool.
    2021-02-16 17:07:05,782 487    [           main] DEBUG        com.keafmd.dao.IUserDao  - Cache Hit Ratio [com.keafmd.dao.IUserDao]: 0.5
    com.keafmd.domain.User@75c072cb
    false
    
    Process finished with exit code 0
    

    经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。
    注意:二级缓存中存放的内容是数据,而不是对象,内容虽然是同样的,但是存放的是数据,第二次虽然没有发起查询,但从二级缓存中获取时,会把内容封装到新的对象返回,所以是false。

    二级缓存注意事项

    当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。

    User:

    package com.keafmd.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    import java.util.List;
    
    /**
     * Keafmd
     *
     * @ClassName: User
     * @Description:
     * @author: 牛哄哄的柯南
     * @date: 2021-02-08 15:16
     */
    
    public class User implements Serializable {
        private Integer id;
        private String username;
        private String sex;
        private String address;
        private Date birthday;
    
        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 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;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
    }
    

    以上就是Mybatis缓存的全部内容。

    看完如果对你有帮助,感谢点赞支持!
    如果你是电脑端的话,看到右下角的 “一键三连” 了吗,没错点它[哈哈]

    在这里插入图片描述

    加油!

    共同努力!

    Keafmd

    展开全文
  • MyBatis 缓存

    2020-08-13 20:59:14
    Mybatis缓存MyBatis 缓存的作用MyBatis 缓存的种类1. 一级缓存:2. 二级缓存:实现(二级缓存的实现)1. 自带:2. 第三方的 ehcache 二级缓存 MyBatis 缓存的作用 使用缓存的作用:减少 Java 应用程序与数据库的...

    MyBatis 缓存的作用


    使用缓存的作用:减少 Java 应用程序与数据库的交互次数,从而提升程序的运行效率。比如第一次查询出某个对象之后,MyBatis 会自动将其存入缓存,当下一次查询同一个对象时,就可以直接从缓存中获取,不必再次访问数据库了。


    MyBatis 缓存的种类


    两种:

    1. 一级缓存
    2. 二级缓存

    1. 一级缓存:

    MyBatis 自带一级缓存,并且是无法关闭的,一直存在,一级缓存的数据存储在 SqlSession 中,即它的作用域是同一个 SqlSession,当使用同一个 SqlSession 对象执行查询的时候,第一次的执行结果会自动存入 SqlSession 缓存,第二次查询时可以直接从缓存中获取。

    注意:

    1. 但是如果是两个 SqlSession 查询两次同样的 SQL,一级缓存不会生效,需要访问两次数据库
    2. 同时需要注意,为了保证数据的一致性,如果 SqlSession 执行了增加、删除,修改操作,MyBatis 会自动清空 SqlSession 缓存中存储的数据。
    3. 一级缓存不需要进行任何配置,可以直接使用。

    2. 二级缓存:

    MyBatis 二级缓存是 Mapper 级别的,只要是同一个 Mapper,无论使用多少个 SqlSession 来操作,数据都是共享的。
    MyBatis 二级缓存默认是关闭的,需要使用时可以通过配置手动开启。


    实现(二级缓存的实现)


    MyBatis 可以使用自带的二级缓存,也可以使用第三方的 ehcache 二级缓存。

    1. 自带:

    1. config.xml 中配置开启二级缓存
    2. xxxMapper.xml 中配置二级缓存
    3. Classes 实体类实现 Serializable 接口
    <configuration>
    
        <!-- 设置 settings -->
        <settings>
            <!-- 开启二级缓存 -->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
    </configuration>
    
    <mapper namespace="...">
    
    	<!-- 配置 -->
        <cache></cache>
    
        <select id="findById" parameterType="long" resultType="....">
            select * from classes where id = #{id}
        </select>
    
    </mapper>
    
    public class xxx implements Serializable {
        // .......
        // .......
    }
    

    2. 第三方的 ehcache 二级缓存

    1. pom.xml 添加 ehcache 相关依赖
    2. 在 resources 路径下创建 ehcache.xml
    3. config.xml 中配置开启二级缓存
    4. xxxMapper.xml 中配置二级缓存
    5. 注意:实体类不需要实现 Serializable 接口
    <dependencies>
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.0.0</version>
      </dependency>
    
      <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache-core</artifactId>
        <version>2.4.3</version>
      </dependency>
    </dependencies>
    
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore/>
        <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    
    <configuration>
    
        <!-- 设置settings -->
        <settings>
            <!-- 开启二级缓存 -->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
    </configuration>
    
    <mapper namespace="...."> 
        <!-- 开启二级缓存 -->
        <cache type="org.mybatis.caches.ehcache.EhcacheCache" >
            <!-- 缓存创建以后,最后一次访问缓存的时间至失效的时间间隔 -->
            <property name="timeToIdleSeconds" value="3600"/>
            <!-- 缓存自创建时间起至失效的时间间隔-->
            <property name="timeToLiveSeconds" value="3600"/>
            <!-- 缓存回收策略,LRU 移除近期最少使用的对象 -->
            <property name="memoryStoreEvictionPolicy" value="LRU"/>
        </cache>
    
        <select id="findById" parameterType="long" resultType="...">
            select * from classes where id = #{id}
        </select>
    
    </mapper>
    
    展开全文
  • mybatis缓存

    2017-12-29 15:05:50
    mybatis缓存

    一级缓存

    • 这里写图片描述
    • mybatis的一级缓存是SQLSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间缓存数据区域(HashMap)是互相不影响的。
    • 一级缓存的作用域是SqlSession范围的,当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存)中,第二次查询时会从缓存中获取数据,不再去底层进行数据库查询,从而提高了查询效率。
    • ==如果SqlSession执行了DML操作(insert、update、delete),并执行commit()操作,mybatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存数据中存储的是最新的信息,避免出现脏读现象==
    • 当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了,Mybatis默认开启一级缓存,不需要进行任何配置。

    一级缓存的验证

    • 第一种方式:同一个代理类对象,两次执行相同的sql查询语句
      • 测试代码:这里写图片描述
      • 结果: 这里写图片描述
      • 总结:通过观察结果可以看出,在第一次查询id为1的Employee对象时执行了一条select语句,但是第二次获取id为1的Employee对象时并没有执行select语句,因为此时一级缓存也就是SqlSession缓存中已经缓存了id为1的Employee对象,Mybatis直接从缓存中将对象取出来,并没有再次去查询数据库,所以第二次也就没有执行select语句
    • 第二种方式:进行事务提交后,缓存清空:
      • 代码 这里写图片描述
      • 结果:这里写图片描述
      • 在第一次查询id为1的employee对象时执行了一条select语句,接下来执行了一个delete并commit操作,Mybatis为了保证缓存中存储的是最新消息,会清空SqlSession缓存。当第二次获取id为1的User对象时一级缓存也就是SqlSession缓存中并没有缓存任何对象,所以Mybatis再次执行语句去查询id为1的Exployee对象
    • 第三中方式:执行一次后,关闭sqlSession
      • 代码:这里写图片描述
      • 结果:这里写图片描述
      • 在第一次查询id为1的User对象时执行了一条select语句,接下来调用SqlSession的close()方法,该方法会关闭SqlSession缓存,当第二次获取id为1的Employee对象时一级缓存也就是SqlSession缓存是一个新的对象,其中并没有缓存任何对象,所以Mybatis再次执行select语句去查询id为1的Employee对象

    二级缓存

    • 二级缓存是mapper级别的缓存,使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMapper进行数据存储,相比一级缓存SqlSession,二级缓存的范围更大,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
    • 二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的SqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
    • ==Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。==
    • 禁用二级缓存

      • 在statement中设置userCache=false可以禁用当前的select语句的二级缓存,及每次查询都会发出sql区查询,默认值是true 这里写图片描述
      • 这样设置了的sqlstatement每次查询会不使用二级缓存
    • 新缓存(清空缓存)

      • 执行完事务提交的方法 后需要刷新缓存,设置flushcache=true表示刷新缓存,这样可以避免脏读

    二级缓存开启

    • 第一步:在mybatis的全局配置文件中这里写图片描述
    • cacheEnabled的value为true表示在此配置文件下开启二级缓存,该属性默认为false。
    • 第二步:需要指定开启二级缓存的namepace的mapper,在mepper映射配置文件中开启这里写图片描述
    • cache元素用来开启当前mapper的namespace下的二级缓存,该元素的属性设置如下:
      • flushInterval:刷新间隔,可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段,默认情况下是不设置的,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
      • size:缓存数目,可以被设置为任意正整数,要缓存对象数目和运行环境可用内存资源数目,默认值是1024.
      • readOnly:只读属性可以被设置为true或false,只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势,可读写的缓存会返回缓存对象的拷贝(通过序列化),这会慢一些,但是安全,因此默认是false。
      • eviction:收回策略,默认为LRU,有如下几种
        • LRU:最近最少使用的策略,移除最长时间不被使用的对象。
        • FIFO:先进先出策略,按对象进入缓存的顺序来移除它们。
        • SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象。
        • WEAK:弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    • ==注意:==
      • 使用二级缓存时,与查询结果映射的java对象必须实现java.io.Serializable接口的序列化和反序列化操作
      • 如果存在父类,其成员都需要实现序列化接口,
      • 实现序列化接口是为了对缓存数据进行序列化和反序列化操作,因为二级缓存数据存储介质多种多样,不一定在内存,有可能是硬盘或者远程服务器。

    二级缓存测试

    • 代码:这里写图片描述
    • 结果:这里写图片描述
    • 分析:第一次查询是关闭了一级缓存,第二次查询从一级缓存中没有找到,就会从二级缓存中找,并且找到了,所以只有一条sql语句

    二级缓存应用场景

    • 对于访问多的请求且用户对查询结果实时性要求不高,此时可以采用mybatis二级缓存技术降低数据库访问量,提高访问的速度,业务场景如:耗时较高的统计分析sql,电话账单查询sql
    • 实现方法如下:通过设置刷新的时间间隔,由mybatis每隔一段时间自动清空缓存,根据数据变化的频率设置刷新缓存的间隔flushinterval,比如设置为30分钟,60分钟,24小时等,根据需求而定

    局限性

    • mybatis对细粒度缓存级别的缓存实现不好,比如;对商品信息进行缓存,由于商品信息查询访问量很大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存,就无法实现当一个商品刷新的时候,只刷新该商品的缓存信息,不刷心其他商品的缓存信息,因为mybatis的二级缓存区域以mapper为单位划分,当一个商品的信息的变化,会将所有的商品的缓存数据全部清空,解决此类问题需要在业务层根据需求对数据有针对性的缓存
    展开全文
  • MyBatis缓存

    2021-01-15 23:12:11
    MyBatis缓存 Mybatis 中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。 一级缓存是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以后的查询不会从...

    MyBatis缓存

    Mybatis 中有一级缓存二级缓存

    一级缓存是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条 SQL。

    二级缓存是指可以跨 SqlSession 的缓存。 是 mapper 级别的缓存,对于 mapper 级别的缓存不同的sqlsession 是可以共享的。

    MyBatis缓存示意图:

    Mybatis缓存示意图

    一级缓存(SqlSession级别)

    第一次发出一个查询 sql, sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一个 map
    key: MapperID+offset+limit+Sql+所有的入参
    value:查询结果

    同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作(修改、添加、删除),本 sqlsession 中的一级缓存区域全部清空。

    配置一级缓存:

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

    在MyBatis的配置文件中,添加上述语句,就可以使用一级缓存。共有两个选项,SESSION或者STATEMENT,默认是SESSION级别,即在一个MyBatis会话中执行的所有语句,都会共享这一个缓存。一种是STATEMENT级别,可以理解为缓存只对当前执行的这一个Statement有效。

    二级缓存(Mapper级别)

    二级缓存的范围是 mapper 级别(mapper 同一个命名空间), mapper 以命名空间为单位创建缓存数据结构,结构是 map。 mybatis 的二级缓存是通过 CacheExecutor 实现的。 CacheExecutor其实是 Executor 的代理对象。所有的查询操作,在 CacheExecutor 中都会先匹配缓存中是否存在,不存在则查询数据库。
    key: MapperID+offset+limit+Sql+所有的入参

    具体使用需要配置:

    1. Mybatis 全局配置中启用二级缓存配置
    2. 在对应的 Mapper.xml 中配置 cache 节点
    3. 在对应的 select 查询节点中添加 useCache=true

    二级缓存除了使用内置的缓存实现,也可以自己通过实现Cache接口来实现。

    配置二级缓存:

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

    在MyBatis的配置文件中,添加上述语句,然后在对应的Mapper文件中添加<cache><cache-ref>标签即可开启二级缓存。

    我的更多文章尽在:我的个人博客

    展开全文
  • myBatis 缓存

    2020-07-19 23:35:53
    这里写目录标题MyBatis缓存介绍mybatis的相关概念一级缓存一级缓存实现二级缓存配置二级缓存脏读的产生脏读的避免集成EhCache缓存集成Redis缓存 MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级...
  • Mybatis 缓存

    2020-10-12 07:47:35
    Mybatis 缓存知识点 Mybatis 中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以后的查询...
  • mybatis 缓存

    2017-11-21 17:31:02
    mybatis 缓存数据模型分析思路 每张表记录的数据内容: 分模块对每张表的内容进行熟悉,相当于学习系统需求功能的过程 每张表的重要字段: 非空字段、外键字段 数据库表与表之间的关系: 外键字段 表与表之间的业务...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,214
精华内容 6,885
关键字:

mybatis缓存