全网最全,Mybatis的缓存机制详解,带给你不一样的体验

2020全网最全最强的MyBatis面试题!(附带答案) 题目大纲 1、什么是MyBatis? 2、讲下MyBatis的缓存 3、Mybatis是如何进行分页的?分页插件的原理是什么? 4、简述Mybatis的插件运行原理,以及如何编写一个插件? 5、Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不? 6、#{}和${}的区别是什么? 7、为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里? 8、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? 9、MyBatis与Hibernate有哪些不 阅读详情

一级缓存

MyBatis 包含了一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。mybatis默认情况下只会开启一级缓存,也就是局部的 session 会话缓存。

首先我们要知道什么是查询缓存?查询缓存又有什么作用?

  • 功能:mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

如下图,每一个 session 会话都会有各自的缓存,这缓存是局部的,也就是所谓的一级缓存:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

一级缓存是SqlSession级别的缓存。我们都知道在操作数据库时需要构造 sqlSession对象,而在sqlSession对象中有一个数据结构(HashMap)用于存储缓存数据。

如下图:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

从图上,我们可以看出,一级缓存区域是根据SqlSession为单位划分的。每次查询都会先从缓存区域找,如果找不到就会从数据库查询数据,然后将查询到的数据写入一级缓存中。Mybatis内部存储缓存使用的是一个HashMap对象,key为 hashCode + sqlId + sql 语句。而value值就是从查询出来映射生成的java对象。而为了保证缓存里面的数据肯定是准确数据避免脏读,每次我们进行数据修改后(增、删、改操作)就会执行commit操作,清空缓存区域。

我们可以来写一个测试用例,测试一下同一个数据的第二次查询是否没有访问数据库,代码如下:

package org.zero01.test;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;
import org.json.JSONObject;
import org.junit.Test;
import org.zero01.dao.StudentMapper;
import org.zero01.pojo.Student;

import java.io.IOException;
import java.io.InputStream;

public class TestMybatisCache {

    @Test
    public void testMybatisCache() throws IOException {
        String confPath = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(confPath);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        // 进行第一次查询
        Student student1 = studentMapper.selectByPrimaryKey(1);
        System.out.println("sqlSession1 第一次查询:" + new JSONObject(student1));

        // 进行第二次查询
        Student student2 = studentMapper.selectByPrimaryKey(1);
        System.out.println("sqlSession1 第二次查询:" + new JSONObject(student2));

        sqlSession.close();
    }
}

控制台打印结果:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

如上,可以看到只有第一次查询访问了数据库。第二次查询则没有访问数据库,是从内存中直接读取出来的数据。

我们上面也提到了,如果进行了增、删、改的sql操作并进行了事务的commit提交操作后,SqlSession中的一级缓存就会被清空,不会导致脏数据的出现。同样的,我们可以使用测试用例来演示这一点,修改测试代码如下:

est
public void testMybatisCache() throws IOException {
    String confPath = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(confPath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

    // 进行第一次查询
    Student student1 = studentMapper.selectByPrimaryKey(2);
    System.out.println("sqlSession1 第一次查询:" + new JSONObject(student1));

    Student stuUpdate = new Student();
    stuUpdate.setSid(2);
    stuUpdate.setSname("渣渣辉");
    stuUpdate.setAge(21);
    int rowCount = studentMapper.updateByPrimaryKeySelective(stuUpdate);
    if (rowCount > 0) {
        sqlSession.commit();
        System.out.println("更新student数据成功");
    }

    // 进行第二次查询
    Student student2 = studentMapper.selectByPrimaryKey(2);
    System.out.println("sqlSession1 第二次查询:" + new JSONObject(student2));

    sqlSession.close();
}

控制台打印结果:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

如上,可以看到当数据更新成功并commit后,会清空SqlSession中的一级缓存,第二次查询就会访问数据库查询最新的数据了。

不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。所以在这种情况下,是不能实现跨表的session共享的。有一点值得注意的是,由于不同的sqlSession之间的缓存数据区域不共享,如果使用多个SqlSession对数据库进行操作时,就会出现脏数据。我们可以修改之前的测试用例来演示这个现象,修改测试代码如下:

@Test
public void testMybatisCache() throws IOException {
    String confPath = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(confPath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();

    // 使用sqlSession1进行第一次查询
    StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
    Student student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第一次查询:" + new JSONObject(student));

    // 使用sqlSession2进行数据的更新
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
    Student student2 = new Student();
    student2.setSid(1);
    student2.setSname("渣渣辉");
    student2.setAge(21);
    int rowCount = studentMapper2.updateByPrimaryKeySelective(student2);
    if (rowCount > 0) {
        sqlSession2.commit();
        System.out.println("sqlSession2 更新student数据成功");
    }

    // 使用sqlSession1进行第二次查询
    student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第二次查询:" + new JSONObject(student));

    sqlSession1.close();
    sqlSession2.close();
}

控制台打印结果:

sqlSession1 第一次查询:{"address":"湖南","sname":"小明","sex":"男","age":16,"sid":1,"cid":1}
sqlSession2 更新student数据成功
sqlSession1 第二次查询:{"address":"湖南","sname":"小明","sex":"男","age":16,"sid":1,"cid":1}

由此可见,Mybatis的一级缓存只存在于SqlSession中,可以提高我们的查询性能,降低数据库压力,但是不能实现多sql的session共享,所以使用多个SqlSession操作数据库会产生脏数据。

二级缓存

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是可以横跨跨SqlSession的。

示意图:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域,如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分,也就是根据命名空间来划分的,如果两个mapper文件的命名空间一样,那样,不同的SqlSession之间就可以共享一个mapper缓存。

示意图:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

在默认情况下是没有开启二级缓存的,除了局部的 session 缓存。而在一级缓存中我们也介绍了,不同的SqlSession之间的一级缓存是不共享的,所以如果我们用两个SqlSession去查询同一个数据,都会往数据库发送sql。这一点,我们也可以通过测试用例进行测试,测试代码如下:

@Test
public void testMybatisCache2() throws IOException {
    String confPath = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(confPath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();

    // 使用sqlSession1进行第一次查询
    StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
    Student student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第一次查询:" + new JSONObject(student));

    sqlSession1.close();

    // 使用sqlSession2进行第一次查询
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
    Student student2 = studentMapper2.selectByPrimaryKey(1);
    System.out.println("sqlSession2 第一次查询:" + new JSONObject(student2));

    sqlSession2.close();
}

控制台输出结果:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

如果想要开启二级缓存,你需要在你的mybatis主配置文件里加入:

<settings>
    <!-- 对在此配置文件下的所有cache进行全局性的开/关设置。默认值:true -->
    <setting name="cacheEnabled" value="true"/>
</settings>

然后在需要被缓存的 SQL 映射文件中添加一行cache配置即可:

...
<mapper namespace="org.zero01.dao.StudentMapper">
    ...
    <cache/>
    ...
</mapper>

字面上看就是这样。这个简单语句的效果如下:

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

注:缓存只适用于缓存标记所在的映射文件中声明的语句。如果你使用的是java的API和XML映射文件一起,默认情况下不会缓存接口中声明的语句。你需要把缓存区使用@CacheNamespaceRef注解进行声明。

假如说,已开启二级缓存的Mapper中有个statement要求禁用怎么办,那也不难,只需要在statement中设置useCache="false"就可以禁用当前select语句的二级缓存,也就是每次都会生成sql去查询,ps:默认情况下默认是true,也就是默认使用二级缓存。如下示例:

<select id="findAll" resultMap="BaseResultMap" useCache="false">
    select
    <include refid="Base_Column_List"/>
    from
    student
</select>

除此之外,还有个flushCache属性,该属性用于刷新缓存,将其设置为 true时,任何时候只要语句被调用,都会导致一级缓存和二级缓存都会被清空,默认值:false。在mapper的同一个namespace中,如果有其他insert、update、delete操作后都需要执行刷新缓存操作,来避免脏读。这时我们只需要设置statement配置中的flushCache="true"属性,就会默认刷新缓存,相反如果是false就不会了。当然,不管开不开缓存刷新功能,你要是手动更改数据库表,那都肯定不能避免脏读的发生。如下示例:

<select id="findAll" resultMap="BaseResultMap" flushCache="true">
    ...
</select>

那既然能够刷新缓存,能定时刷新吗?也就是设置时间间隔来刷新缓存,答案是肯定的。我们在mapper映射文件中添加<cache/>来表示开启缓存,所以我们就可以通过<cache/>元素的属性来进行配置。比如:

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

这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。

  • flushInterval(刷新间隔) 可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
  • size(引用数目) 可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
  • readOnly(只读) 属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。

可用的收回策略有:

  • LRU – 最近最少使用的:移除最长时间不被使用的对象。(默认)
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

开启了二级缓存之后,我们再来进行测试,但是在运行测试用例之前,我们需要给pojo类加上实现序列化接口的代码,不然在关闭SqlSession的时候就会报错,代码如下:

package org.zero01.pojo;

import java.io.Serializable;

public class Student implements Serializable {
    ...
}

测试代码不变,运行后,控制台输出结果如下:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

可以看到,开启二级缓存后,SqlSession之间的数据就可以通过二级缓存共享了,和一级缓存一样,当执行了insert、update、delete等操作并commit提交后就会清空二级缓存区域。当一级缓存和二级缓存同时存在时,会先访问二级缓存,再去访问各自的一级缓存,如果都没有需要的数据,才会往数据库发送sql进行查询。这一点,我们也可以通过测试用例来进行测试,测试代码如下:

@Test
public void testMybatisCache() throws IOException {
    String confPath = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(confPath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();

    // 使用sqlSession1进行第一次查询
    StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
    Student student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第一次查询:" + new JSONObject(student));

    // 使用sqlSession2进行数据的更新
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
    Student student2 = new Student();
    student2.setSid(1);
    student2.setSname("小明");
    student2.setAge(16);
    int rowCount = studentMapper2.updateByPrimaryKeySelective(student2);
    if (rowCount > 0) {
        sqlSession2.commit();
        System.out.println("sqlSession2 更新student数据成功");
    }

    // 使用sqlSession1进行第二次查询
    student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第二次查询:" + new JSONObject(student));

    // 使用sqlSession2进行第一次查询
    student2 = studentMapper2.selectByPrimaryKey(1);
    System.out.println("sqlSession2 第一次查询:" + new JSONObject(student2));

    // 关闭会话
    sqlSession1.close();
    sqlSession2.close();
}

运行测试代码后,控制台输出结果如下:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

通过此测试用例可以看出两点:

  • 1.Mybatis的二级缓存是跨Session的,每个Mapper享有同一个二级缓存域,同样,每次执行commit操作之后,会清空二级缓存区域。
  • 2.如果数据存在一级缓存的话,依旧会去一级缓存中读取数据,这样会发生脏读现象,不过我们可以在相应的statement中,设置flushCache="true",这样每次都会清除缓存,并向数据发送sql来进行查询。

或者全局关闭本地、二级缓存:

<settings>
    <setting name="cacheEnabled" value="false"/>
    <setting name="localCacheScope" value="STATEMENT"/>
</settings>

但是在使用多个sqlSession操作数据库的时候,还有一个需要注意的问题,那就是事务隔离级别,mysql的默认事务隔离级别是REPEATABLE-READ(可重复读)。这样当多个sqlsession操作同一个数据的时候,可能会导致两个不同的事务查询出来的数据不一致,例如,sqlsession1 在同一个事务中读取了两次数据,而 sqlsession2 在 sqlsession1 第一次查询之后就更新了数据,那么由于可重复读的原因,sqlsession1 第二次查询到的依旧是之前的数据。

我们可以使用测试用例来测试一下,首先得关闭缓存或者在相应的statement中设置flushCache属性值为true(此时没有缓存),测试用例代码如下:

@Test
public void testMybatisCache() throws IOException {
    String confPath = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(confPath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();

    // 使用sqlSession1进行第一次查询
    StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
    Student student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第一次查询:" + new JSONObject(student));

    // 使用sqlSession2进行数据的更新
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
    Student student2 = new Student();
    student2.setSid(1);
    student2.setSname("小明");
    student2.setAge(16);
    int rowCount = studentMapper2.updateByPrimaryKeySelective(student2);
    if (rowCount > 0) {
        sqlSession2.commit();
        System.out.println("sqlSession2 更新student数据成功");
    }

    // 使用sqlSession1进行第二次查询
    student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第二次查询:" + new JSONObject(student));

    // 使用sqlSession2进行第一次查询
    student2 = studentMapper2.selectByPrimaryKey(1);
    System.out.println("sqlSession2 第一次查询:" + new JSONObject(student2));

    // 关闭会话
    sqlSession1.close();
    sqlSession2.close();
}

控制台输出结果:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

这就是mysql默认事务隔离级别REPEATABLE-READ(可重复读)导致的现象,这种隔离级别能够保证同一个事务的生命周期内,读取的数据是一致的,但是两个不同的事务之间读取出来的数据就可能不一致。

不过,如果你希望在不同的事务的生命周期内读取的数据一致的话,就需要把事务隔离级别改成READ-COMMITTED(读已提交),该级别会导致不可重复读,也就是说在同一个事务的生命周期内读取到的数据可能是不一致的,而在两个不同的事务之间读取的数据则是一致的。同样的我们可以使用测试用例进行测试,修改测试代码如下:

@Test
public void testMybatisCache() throws IOException {
    String confPath = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(confPath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    // 设置事务隔离级别为读已提交
    SqlSession sqlSession1 = sqlSessionFactory.openSession(TransactionIsolationLevel.READ_COMMITTED);
    SqlSession sqlSession2 = sqlSessionFactory.openSession(TransactionIsolationLevel.READ_COMMITTED);

    // 使用sqlSession1进行第一次查询
    StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
    Student student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第一次查询:" + new JSONObject(student));

    // 使用sqlSession2进行数据的更新
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
    Student student2 = new Student();
    student2.setSid(1);
    student2.setSname("阿基米德");
    student2.setAge(22);
    int rowCount = studentMapper2.updateByPrimaryKeySelective(student2);
    if (rowCount > 0) {
        sqlSession2.commit();
        System.out.println("sqlSession2 更新student数据成功");
    }

    // 使用sqlSession1进行第二次查询
    student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第二次查询:" + new JSONObject(student));

    // 使用sqlSession2进行第一次查询
    student2 = studentMapper2.selectByPrimaryKey(1);
    System.out.println("sqlSession2 第一次查询:" + new JSONObject(student2));

    // 关闭会话
    sqlSession1.close();
    sqlSession2.close();
}

控制台输出结果:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

可以看到,设置成读已提交后,两个事务在数据更新后查询出来的数据是一致的了。至于是使用可重复读还是读已提交,就取决于实际的业务需求了,如果希望同一个事务的生命周期内,读取的数据是一致的,就使用可重复读级别。如果希望两个不同的事务之间查询出来的数据是一致的,那么就使用读已提交级别。

自定义缓存

mybatis自身的缓存做的并不完美,不过除了使用mybatis自带的二级缓存, 你也可以使用你自己实现的缓存或者其他第三方的缓存方案创建适配器来完全覆盖缓存行为。所以它提供了使用自定义缓存的机会,我们可以选择使用我们喜欢的自定义缓存,下面将介绍一下,使用ehcache作为mybatis的自定义缓存的具体步骤。

首先,要想使用mybatis自定义缓存,就必须让自定义缓存类实现mybatis提供的Cache 接口(org.apache.ibatis.cache.Cache):

public interface Cache {
  // 获取缓存编号
  String getId();

  // 获取缓存对象的大小
  int getSize();

  // 保存key值缓存对象
  void putObject(Object key, Object value);

  // 通过kEY获取值
  Object getObject(Object key);

  // 缓存中是否有某个key
  boolean hasKey(Object key);

  // 获取缓存的读写锁
  ReadWriteLock getReadWriteLock();

  // 通过key删除缓存对象
  Object removeObject(Object key);

  // 清空缓存
  void clear();
}

我们要使用ehcache做自定义缓存,就应该完成这个自定义缓存类,但mybatis的git上提供了相对于的适配包,我们只需要下载即可,下面是适配包的maven依赖:

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>

接着在相应的 mapper xml文件中配置相应的缓存实现类:

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

实现Cache接口的是EhcacheCache的父类AbstractEhcacheCache,我们可以看一下它的源码:

package org.mybatis.caches.ehcache;

import java.util.concurrent.locks.ReadWriteLock;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.apache.ibatis.cache.Cache;

public abstract class AbstractEhcacheCache implements Cache {
    protected static CacheManager CACHE_MANAGER = CacheManager.create();
    protected final String id;
    protected Ehcache cache;

    public AbstractEhcacheCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        } else {
            this.id = id;
        }
    }

    public void clear() {
        this.cache.removeAll();
    }

    public String getId() {
        return this.id;
    }

    public Object getObject(Object key) {
        Element cachedElement = this.cache.get(key);
        return cachedElement == null ? null : cachedElement.getObjectValue();
    }

    public int getSize() {
        return this.cache.getSize();
    }

    public void putObject(Object key, Object value) {
        this.cache.put(new Element(key, value));
    }

    public Object removeObject(Object key) {
        Object obj = this.getObject(key);
        this.cache.remove(key);
        return obj;
    }

    public void unlock(Object key) {
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (obj == null) {
            return false;
        } else if (!(obj instanceof Cache)) {
            return false;
        } else {
            Cache otherCache = (Cache)obj;
            return this.id.equals(otherCache.getId());
        }
    }

    public int hashCode() {
        return this.id.hashCode();
    }

    public ReadWriteLock getReadWriteLock() {
        return null;
    }

    public String toString() {
        return "EHCache {" + this.id + "}";
    }

    public void setTimeToIdleSeconds(long timeToIdleSeconds) {
        this.cache.getCacheConfiguration().setTimeToIdleSeconds(timeToIdleSeconds);
    }

    public void setTimeToLiveSeconds(long timeToLiveSeconds) {
        this.cache.getCacheConfiguration().setTimeToLiveSeconds(timeToLiveSeconds);
    }

    public void setMaxEntriesLocalHeap(long maxEntriesLocalHeap) {
        this.cache.getCacheConfiguration().setMaxEntriesLocalHeap(maxEntriesLocalHeap);
    }

    public void setMaxEntriesLocalDisk(long maxEntriesLocalDisk) {
        this.cache.getCacheConfiguration().setMaxEntriesLocalDisk(maxEntriesLocalDisk);
    }

    public void setMemoryStoreEvictionPolicy(String memoryStoreEvictionPolicy) {
        this.cache.getCacheConfiguration().setMemoryStoreEvictionPolicy(memoryStoreEvictionPolicy);
    }
}

接着我们还需要在resources目录下,创建ehcache的配置文件:ehcache.xml,文件内容如下:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="java.io.tmpdir"/>

    <!--
    Mandatory Default Cache configuration. These settings will be applied to caches
    created programmtically using CacheManager.add(String cacheName)
    -->
    <!--
       name:缓存名称。
       maxElementsInMemory:缓存最大个数。
       eternal:对象是否永久有效,一但设置了,timeout将不起作用。
       timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
       timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
       overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
       diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
       maxElementsOnDisk:硬盘最大缓存个数。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
       clearOnFlush:内存数量最大时是否清除。
    -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="5"
            timeToLiveSeconds="5"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
    />
</ehcache>

以上就完成了自定义缓存的配置,接下来我们测试一下缓存是否生效,测试代码如下:

@Test
public void testMybatisCache2() throws IOException {
    String confPath = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(confPath);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();

    // 使用sqlSession1进行第一次查询
    StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
    Student student = studentMapper.selectByPrimaryKey(1);
    System.out.println("sqlSession1 第一次查询:" + new JSONObject(student));

    sqlSession1.close();

    // 使用sqlSession2进行第一次查询
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
    Student student2 = studentMapper2.selectByPrimaryKey(1);
    System.out.println("sqlSession2 第一次查询:" + new JSONObject(student2));

    sqlSession2.close();
}

控制台输出结果:

全网最全,Mybatis的缓存机制详解,带给你不一样的体验

 

可以看到,sqlsession2 查询数据的时候缓存命中率为0.5,并且也没有向数据库发送sql语句,那么就代表我们配置的自定义缓存生效并可以成功缓存数据了。

MyBatis从前世到今生一网打尽(全网最全,建议收藏) 一、框架概述 1.1 软件开发常用结构 1.1.1、三层架构 ​ 三层架构包含的三层: 界面层(User Interface layer) 业务逻辑层(Business Logic Layer) 数据访问层(Data access layer) ​ 三层架构分别的职责是: 界面层(表示层,视图层):主要功能是接受用户的数据,显示请求的处理结果。使用 web 页面和 用户交互,手机 app 也就是表示层的,用户在 app 中操作,业务逻辑在服务器端处理。 业务逻辑层:接收表示传 阅读详情

相关推荐

Mybatis 系列全解(3)——全网免费最细最全,手把手教,学完就可做项目!

什么是动态SQL :根据不同条件生成不同的 SQL 语句。如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。ifforeach1)什么是缓存Cache?存在内存中的临时数据。

坚刚不可夺志的王先生博客 1000

mybatis缓存

Mybatis的缓存,包括一级缓存和二级缓存 一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。 二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是一级缓...

之死靡它 160

SAP 物料账前期没有运行的解决方法

If you use the actual costing (material ledger), you must have completely closed all period-end closing operations in the actual costing for the penultimate period prior to the period shift. Use the '...

含笑饮砒霜死后更嚣张 3319

MyBatis中调用SqlSession.commit()和SqlSession.close()对二级缓存的影响

  在学习MyBatis时,我一直对进行什么操作会影响数据放进二级缓存的情况感到非常疑惑。由此,我特地对各个情况进行测试分析。特别是在分析SqlSession的commit()和close()方法对二级缓存的影响时,花了我好多的时间。只追求最终结果的朋友,可以直接拉到最后看我的总结。 Mapper: &lt;?xml version="1.0" encoding="UTF-8" ?&gt;...

Parker的博客 2万+

Docker拉取ubuntu22.04镜像使用ROS2 humble及仿真工具可视化进行导航

Docker中安装Ubun22以及ROS,gazebo可视化等

lovebc的博客 1678

PCB设计中的规则优先级陷阱:为什么你的过孔总是十字连接?

本文深入解析PCB设计中Altium Designer规则优先级机制,解决过孔总是十字连接而非全连接的问题。通过分析铺铜连接原理和规则匹配逻辑,提供精确控制连接方式的技术实践,包括Custom Query使用和优先级最佳设置,帮助设计师避免常见陷阱,优化高速电路板的电气性能和制造可靠性。

banana的博客 469

mybatis的缓存

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

weixin_41605945的博客 162

Ansys Lumerical FDTD仿真加速策略与高性能硬件选型指南

本文深入探讨了Ansys Lumerical FDTD仿真的核心加速策略,涵盖算法优化与硬件选型两大维度。通过合理运用非均匀网格、对称边界条件等算法技巧,并精准匹配CPU/GPU计算资源与高性能硬件配置,可显著提升仿真效率,将计算时间从数天缩短至数小时。

a0b1c2d3的专栏 624

批量查询preparedstatement只需要最后关闭还是每次都关闭_连mybits工作原理都不懂,你敢说你自己会java?...

一、相较于Hibernate,mybatis的优势在哪里?Hibernate缺点:1、运行效率低,内存占用比较严重2、针对单一对象的增删改查,适合Hibernate,而Hibernate在批量操作时处于弱势3、虽然Hibernate引入一二级缓存、lazyload、查询缓存等更多优化空间(对于那些改动 不大且经常使用的数据,可将他们放入缓存中),但Hibernate对于持久层封装过于完 整,导致开...

weixin_39916511的博客 1081

5.3-集群电源模式解析

DSU-120 DynamIQ™集群支持9种电源模式,包括正常运行(ON)、完全断电(OFF)、三种数据保持模式(FUNC_RET、FULL_RET、MEM_RET)、两种调试模拟模式(OFF_EMU、MEM_RET_EMU),以及系统恢复模式(WARM_RST、DBG_RECOV)。这些模式通过智能管理电源状态,在保持数据完整性的同时实现功耗优化,并为系统调试和故障恢复提供支持。不同模式在RAM保持、逻辑断电和调试能力方面各有侧重,满足从正常运行到深度调试等不同场景需求。

baron-周贺贺-代码改变世界ctw 117

FortiOS 存在身份验证绕过导致命令执行漏洞(CVE-2024-55591)

FortiOS 是 Fortinet 公司开发的操作系统,作为 Fortinet 安全平台的核心,提供全面的网络安全功能。 它将网络和安全功能无缝集成,支持跨本地、云、混合和融合 IT/OT/IoT 基础设施环境的统一管理。 FortiOS 7.6 版本引入了生成式人工智能助手、数据保护、托管服务和单一代理等新功能,进一步增强了其安全性和性能。

缘梦未来的博客 1843

Mybatis之缓存详解

Mybatis之缓存详解

NestorBian的博客 1161

Python爬虫实战:链家/贝壳租房数据抓取与可视化分析平台

本文详细介绍了如何使用Python爬虫技术抓取链家/贝壳网站的租房信息,并构建一个完整的数据可视化分析平台。文章涵盖了从数据抓取、数据清洗、数据存储到数据可视化的全流程,使用最新的Python技术栈,包括AsyncIOPandasMongoDB和Pyecharts等库。本系统可作为毕业设计项目,提供了完整的代码实现和架构设计。关键词:Python爬虫、数据抓取、数据可视化、链家、贝壳、租房信息、毕业设计。

2201_76125261的博客 1208

mybatis缓存机制_全网最全,Mybatis的缓存机制详解,带给你不一样的体验

一级缓存MyBatis 包含了一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。mybatis默认情况下只会开启一级缓存,也就是局部的 session 会话缓存。首先我们要知道什么是查询缓存?查询缓存又有什么作用?功能:mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。如下图,每一个 sessio...

weixin_39620151的博客 470

Python全栈入门到实战【基础篇 04】核心基础:变量与4种基础数据类型(int/float/str/bool)

本文介绍了Python编程中的变量和四种基础数据类型,作为后续所有语法的入门前提。变量是数据的“容器”,解决数据复用问题,而数据类型决定数据能进行的操作。文章详细讲解了变量的定义语法、命名规则(强制语法和推荐风格)、赋值与修改原理,并通过实操示例展示其应用场景。正确使用变量可以提升代码复用性、简化修改并增强可读性,是编程效率的核心原则。掌握这些基础知识能避免90%的新手语法错误,为学习更复杂的Python语法打下坚实基础。

那些看似不起波澜的日复一日,会在某天让你看到坚持的意义 4328

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

mybatis 存储 jsonobject fastjason_全网最全,Mybatis的缓存机制详解,带给你不一样的体验...

一级缓存MyBatis 包含了一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。mybatis默认情况下只会开启一级缓存,也就是局部的 session 会话缓存。首先我们要知道什么是查询缓存?查询缓存又有什么作用?功能:mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。如下图,每一个 sessio...

weixin_32350433的博客 1024

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Mybatis的缓存机制 ----全网最详解析

Mybatis框架的一级缓存又称之为会话(Session)缓存,默认是开启的,且无法关闭!一级缓存必须满足:同一个SqlSession、同一个Mapper对象、执行相同的查询、且参数相同!

m0_53856016的博客 535

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

全网最全MyBatis基于XML使用系列四:缓存,腾讯T3大牛亲自讲解

特点:1.默认就开启了,也可以关闭一级缓存 localCacheScope=STATEMENT2.作用域:是基于sqlSession(默认),一次数据库操作会话。3.缓存默认实现类PerpetualCache ,使用map进行存储的4.查询完就会进行存储5.先从二级缓存中获取,再从一级缓存中获取 * key==> sqlid+sql失效情况:1.不同的sqlSession会使一级缓存失效2.同一个SqlSession,但是查询语句不一样。

2401_83739821的博客 544

mapper命名规范_Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件

封面:洛小汐 作者:潘潘 若不是生活所迫,谁愿意背负一身才华。前言上节我们介绍了 《 Mybatis系列全解(四):全网最全!Mybatis配置文件 XML 全貌详解 》,内容很详细( 也很枯燥),由于篇幅实在过于冗长,我预计大家想看完得花上两段上班地铁公交车的时间 。。。不过应该有让大家了解到 Mybatis 的核心配置文件 config.xml 全貌,其中的 元素即是我们本节准备登场介绍的 ...

weixin_39650745的博客 1660

全网最全!MyBatis vs Hibernate:究竟该怎么选?

fill:#333;important;important;fill:none;MyBatis半自动ORMSQL手写轻量级复杂查询强学习简单SQL可控代表作: 阿里系Hibernate全自动ORMSQL自动生成重量级CRUD高效学习曲线陡对象关系映射强代表作: 企业级ERP对比维度SQL控制: MyBatis胜开发效率: Hibernate胜性能调优: MyBatis胜移植性: Hibernate胜学习曲线: MyBatis胜缓存: Hibernate更复杂。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1209

i了i了,这简直就是2020全网最全最强的MyBatis面试题!

题目大纲 1、什么是MyBatis? 2、讲下MyBatis的缓存 3、Mybatis是如何进行分页的?分页插件的原理是什么? 4、简述Mybatis的插件运行原理,以及如何编写一个插件? 5、Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不? 6、#{}和${}的区别是什么? 7、为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里? 8、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? 9、MyBatis与Hibernate有哪些不

w1103576的博客 2262

mybatis delete 存在id单删除不掉_Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件...

封面:洛小汐作者:潘潘若不是生活所迫,谁愿意背负一身才华。前言上节我们介绍了 《Mybatis系列全解(四):全网最全!Mybatis配置文件 XML 全貌详解》,内容很详细( 也很枯燥),由于篇幅实在过于冗长,我预计大家想看完得花上两段上班地铁公交车的时间 。。。不过应该有让大家了解到 Mybatis 的核心配置文件config.xml全貌,其中的 元素即是我们本节准备登场...

weixin_39921023的博客 454

全网最全mybatis笔记,入门到精通,图文并茂,面试必备

简介 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。 如何学习 国内图书市场上没有介绍Mybatis的书籍,有的只是官方的API和少数的几篇博客文章,国外图书市场上的这类书籍也是凤毛麟角,这使得系统学习

qwe123147369的博客 307

后端开发必备全网最全MyBatis面试题(25秋招走起)

MyBatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态SQL,可以严格控制SQL执行性能,灵活度高。MyBatis 提供了与数据库交互的API,让程序员可以通过简单的XML或注解来配置和映射原生信息,将接口和Java的实体类映射到数据库中的记录,免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

Wangzc_1116的博客 431
上一篇: 初识Kafka,基础剖析再深入,助你游走在数据时代前沿
下一篇: 深入理解企业总体架构,看这篇就够了
Java领域指导者
博客等级 码龄7年 404粉丝 265原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值