一. 一级缓存
这里以《Mybatis:动态SQL》一文中的Book的相关数据为例,演示一下一级缓存:
测试类:
package io.zhangjia.util; import com.alibaba.fastjson.JSON; import io.zhangjia.entity.Address; import io.zhangjia.entity.Order; import io.zhangjia.entity.Product; import io.zhangjia.mapper.BookMapper; import io.zhangjia.mapper.OrderMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class Main { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = build.openSession(true); BookMapper mapper = sqlSession.getMapper(BookMapper.class); // 第一次查询 long start = System.currentTimeMillis(); mapper.queryById(1); long end = System.currentTimeMillis(); System.out.println("第一次查询耗时" + (end - start) + "毫秒"); System.out.println("-------------------分隔线-------------------"); // 第二次查询 start = System.currentTimeMillis(); mapper.queryById(1); end = System.currentTimeMillis(); System.out.println("第二次查询耗时" + (end - start) + "毫秒"); sqlSession.close(); inputStream.close(); } }
通过下图的日志我们可以看到,虽然测试类执行了两次查询操作,但是却只有第一次查询执行了sql语句,这便是Mybatis的一级缓存机制。
Mybatis的一级缓存默认开启,作用于sqLSession,同一个sql Session连续查询同一条数据,只有第一次会发起SELECT查询,但是如果在两次查询中间有发生数据更新(增删改)操作,则会清空缓存:
public class Main { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = build.openSession(true); BookMapper mapper = sqlSession.getMapper(BookMapper.class); // 第一次查询 Book book = mapper.queryById(1); System.out.println("-------------------分隔线-------------------"); book.setBookPrice(10.1); mapper.doUpdate(book); //更新数据 // 第二次查询 mapper.queryById(1); sqlSession.close(); inputStream.close(); } }
可以看到,因为两次查询中间执行了更新操作,所以即使查询的是同一条数据,依旧执行了两次sql语句。
二. 二级缓存
在上一节我们说过,Mybatis的一级缓存默认开启,并且作用于sqLSession,我们可以通过下面的例子来验证上述结论:
package io.zhangjia.util; import com.alibaba.fastjson.JSON; import io.zhangjia.entity.Address; import io.zhangjia.entity.Book; import io.zhangjia.entity.Order; import io.zhangjia.entity.Product; import io.zhangjia.mapper.BookMapper; import io.zhangjia.mapper.OrderMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class Main { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = build.openSession(true); BookMapper mapper = sqlSession.getMapper(BookMapper.class); // 第一次查询 Book book = mapper.queryById(1); sqlSession.close(); System.out.println("-------------------分隔线-------------------"); SqlSession sqlSession2 = build.openSession(true); BookMapper mapper2 = sqlSession2.getMapper(BookMapper.class); // 第二次查询 mapper2.queryById(1); sqlSession2.close(); inputStream.close(); } }
在上面的测试类中,我们的两次查询操作分别使用了不同的sqlSession,可以看到,因为一级缓存作用于sqLSession,所以即使两次查询的是同一条数据,但是仍然会执行两次查询语句:
我们可以通过开启Mybatis的二级缓存来解决这个问题。要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:<cache/>即可:
除了修改SQL映射文件,还需要将Book的实体类实现Serializable接口
public class Book implements Serializable { ...... }
Mybatis二级缓存作用于SessionFactory,如果两次查询基于同一个SessionFactory,那么就从二级缓存中取数据,而不用到数据库里去取了,我们上面的sqlSession1和sqlSession2都是基于同一个SessionFactory创建的,所以开启二级缓存后,两次查询即使使用不同的sqlSession也只会执行一次SQL语句。
最后我们可以在Mapper.xml中,为select标签添加useCache属性来决定该查询是否使用缓存
<select useCache=='false' id="queryByAddressId" parameterType="int"resultType="address"> SELECT * FROM address WHERE address_id=#{addressId} </select>
请登录之后再进行评论