本文详细介绍了Mybatis二级缓存的基本概念、工作原理及其优势,包括如何在配置文件中开启二级缓存,以及二级缓存的使用场景和配置选项。文章还讨论了在使用二级缓存时可能遇到的问题及解决方法,并通过实战演练展示了二级缓存的实际应用效果。
Mybatis二级缓存也被称为共享缓存模式,它在同一个SqlSessionFactory(负责创建SqlSession对象)中是全局共享的。二级缓存是基于命名空间的,不同的mapper.xml文件中的namespace会对应不同的缓存,因此不同的mapper.xml文件中的查询结果也可以通过二级缓存进行共享。
Mybatis的缓存分为一级缓存和二级缓存。一级缓存是SqlSession级别的缓存,是默认开启的,当执行查询语句后,会先从SqlSession的缓存中查找是否有对应的查询结果,如果查找到了,就直接从缓存中返回结果,而不会去数据库查询。而二级缓存是基于Mapper的缓存,需要手动开启。当SqlSession中查找缓存的结果为空时,才会去二级缓存中查找。
在mybatis的配置文件中,可以通过<setting>
标签的cacheEnabled
属性来开启全局的二级缓存。此外,还需要在mapper.xml文件中开启具体的Mapper对应的缓存,可以通过<cache>
标签来实现。
以下是一个常见的mybatis全局配置文件mybatis-config.xml
的示例,其中开启了全局的二级缓存:
<configuration> <settings> <setting name="cacheEnabled" value="true"/> </settings> <mappers> <mapper resource="com/example/mybatis/UserMapper.xml"/> </mappers> </configuration>
在mapper.xml中开启二级缓存,例如UserMapper.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.example.mybatis.UserMapper"> <cache /> <select id="selectUserById" resultType="User"> SELECT * FROM Users WHERE id = #{id} </select> </mapper>
Mybatis的二级缓存默认是没有过期时间的,即一旦缓存了数据,除非显式地删除缓存,否则会一直存在。可以通过自定义缓存实现类来自定义缓存策略,例如通过Ehcache
等第三方缓存组件来实现缓存过期。以下是使用Ehcache
的示例配置:
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
默认情况下,当插入、更新或删除数据时,二级缓存会被自动刷新,即缓存会被标记为无效。可以通过自定义缓存实现类来实现更复杂的刷新策略。以下是一个自定义缓存实现类的示例:
public class CustomCache implements Cache { private Ehcache ehcache; public CustomCache(String id) { ehcache = new EhcacheFactory().createCache(id); } @Override public Object getObject(Object key) { Element element = ehcache.get(key); return element == null ? null : element.getObjectValue(); } @Override public Object putObject(Object key, Object value) { ehcache.put(new Element(key, value)); return value; } @Override public Object removeObject(Object key) { Element element = ehcache.get(key); if (element != null) { ehcache.remove(element); } return element == null ? null : element.getObjectValue(); } @Override public void clear() { ehcache.removeAll(); } @Override public int getSize() { return ehcache.getSize(); } @Override public String getId() { return ehcache.getCacheName(); } }
当缓存被刷新后,可能会导致缓存中的数据和数据库中的数据不一致。为了解决这个问题,可以在插入、更新或删除数据时,使用flushCache
方法强制刷新缓存,或者使用useCache
属性来控制是否使用缓存。以下是一个示例:
<insert id="insertUser" flushCache="true"> INSERT INTO Users (id, name, email) VALUES (#{id}, #{name}, #{email}) </insert>
当缓存失效时,Mybatis会从数据库中重新查询数据。如果查询操作非常频繁,可能会导致数据库压力过大。可以通过调整缓存过期时间,或者使用分布式缓存来提高缓存命中率。以下是一个调整缓存过期时间的示例:
<cache type="org.mybatis.caches.ehcache.EhcacheCache"> <property name="timeToLiveSeconds" value="600"/> </cache>
以下是一个简单的例子,演示了如何在Mybatis中使用二级缓存。假设我们有一个UserMapper
接口,用于查询用户信息:
public interface UserMapper { User selectUserById(int id); }
对应的mapper.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.example.mybatis.UserMapper"> <cache /> <select id="selectUserById" resultType="User"> SELECT * FROM Users WHERE id = #{id} </select> </mapper>
在应用程序中使用SqlSession
进行查询:
public class Main { public static void main(String[] args) { SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml")); SqlSession session = factory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); // 模拟其他SqlSession查询相同的用户数据 SqlSession session2 = factory.openSession(); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user2 = mapper2.selectUserById(1); System.out.println(user2); } }
通过上述代码,可以看到用户数据只从数据库查询了一次,第二次查询时直接从缓存中获取了数据,展示了二级缓存的高效性。
<mapper namespace="com.example.mybatis.UserMapper"> <select id="selectUserById" resultType="User"> SELECT * FROM Users WHERE id = #{id} </select> </mapper>
修正:
<mapper namespace="com.example.mybatis.UserMapper"> <cache /> <select id="selectUserById" resultType="User"> SELECT * FROM Users WHERE id = #{id} </select> </mapper>
<mapper namespace="com.example.mybatis.UserMapper"> <cache /> <insert id="insertUser" flushCache="false"> INSERT INTO Users (id, name, email) VALUES (#{id}, #{name}, #{email}) </insert> </mapper>
修正:
<mapper namespace="com.example.mybatis.UserMapper"> <cache /> <insert id="insertUser" flushCache="true"> INSERT INTO Users (id, name, email) VALUES (#{id}, #{name}, #{email}) </insert> </mapper>