0065 MyBatis一级缓存与二级缓存
数据库中数据虽多,但访问频率却不同,有的数据1s内就会有多次访问,而有些数据几天都没人查询,这时候就可以将访问频率高的数据放到缓存中,就不用去数据库里取了,提高了效率还节约了数据库资源
MyBatis有两种缓存,分为一级和二级缓存。
一级缓存作用域为SqlSession,同一个SqlSession才能访问到,采用HashMap存储数据,当该SqlSession进行了DML操作或者调用close()方法,其中的一级缓存就会清空。一级缓存默认开启。
二级缓存的作用域是mapper映射配置文件,多个SqlSession共用二级缓存,二级缓存仍然使用HashMap存储数据,如果多个SqlSession执行同一个命名空间的同一条sql,且传入的参数也相同,就是说最终执行相同的sql的话,就会从二级缓存中拿数据。二级缓存需要配置,默认不开启
一级缓存
sql映射器配置xml文件:
<mapper namespace="net.sonng.mbt.mapper.BookMapper">
<select id="findAll" resultType="net.sonng.mbt.entity.Book">
SELECT * FROM book
</select>
<select id="findBookById" parameterType="int" resultType="net.sonng.mbt.entity.Book">
SELECT * FROM book WHERE id=#{id}
</select>
<update id="updateBook" parameterType="net.sonng.mbt.entity.Book" >
UPDATE book
<set>
<if test="name!=null" >`name`=#{name},</if>
<if test="press!=null" >press=#{press},</if>
<if test="author!=null" >author=#{author},</if>
<if test="isbn!=null" >isbn=#{isbn},</if>
<if test="douban!=null" >douban=#{douban}</if>
</set>
WHERE id=${id}
</update>
<select id="findBookInId" parameterType="list" resultType="net.sonng.mbt.entity.Book">
SELECT * FROM book WHERE id In
<foreach item="id" index="i" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>
映射器接口略
测试类:
package net.sonng.mbt.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.sonng.mbt.entity.Book;
import net.sonng.mbt.mapper.BookMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class BookTest {
public static void main(String[] args) throws IOException{
InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session=sqlSessionFactory.openSession();
BookMapper bookMapper=session.getMapper(BookMapper.class);
Book book0=bookMapper.findBookById(5); //第一次查询
System.out.println(book0);
Book book1=bookMapper.findBookById(5); //同样的语句,第二次查询
System.out.println(book1);
book1.setDouban(9.9f);
bookMapper.updateBook(book1); //DML语句,更改了数据
session.commit(); //注意这里,有没有commit效果一样,都会更新数据库,清除一级缓存
Book book2=bookMapper.findBookById(5); //同样的语句,第三次查询
System.out.println(book2);
session.close();
}
}
输出如下:
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=? //
第一次查询
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=9.9]
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=9.9] //第二次查询,没有执行sql语句,直接从SqlSession一级缓存中拿到数据
DEBUG [main] - ==> Preparing: UPDATE book SETname
=?, press=?, author=?, isbn=?, douban=? WHERE id=5 //更新了数据
DEBUG [main] - > Parameters: 深入理解Java 7 核心技术与最佳实践(String), 机械工业出版社(String), 成富著(String), 9787111380399(String), 9.9(Float) //update语句清空了一级缓存
DEBUG [main] - < Updates: 1
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=? //第三次查询,一级缓存已被情况,执行sql语句入库查询
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=9.9]
测试结论之一:不管前面的查询有没有提交事务,查询结果都会写到一级缓存中;如果session执行了DML,一级缓存会被清空
特别注意的是:要是同一条sql语句
才会从缓存中拿数据,比如看下面的测试代码:
package net.sonng.mbt.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.sonng.mbt.entity.Book;
import net.sonng.mbt.mapper.BookMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class BookTest {
public static void main(String[] args) throws IOException{
InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session=sqlSessionFactory.openSession();
BookMapper bookMapper=session.getMapper(BookMapper.class);
List<Integer> ids=new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(4);
ids.add(5);
List<Book> books0=bookMapper.findBookInId(ids); //第一次查询
for(Book b:books0){
System.out.println(b);
}
List<Book> books1=bookMapper.findBookInId(ids); //同一条语句,第二次查询
for(Book b:books1){
System.out.println(b);
}
Book book0=bookMapper.findBookById(5); //虽然缓存中有这条记录,但还是执行了一次sql查询
System.out.println(book0);
session.close();
}
}
输出如下:
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id In ( ? , ? , ? , ? ) //第一次查询
DEBUG [main] - > Parameters: 1(Integer), 2(Integer), 4(Integer), 5(Integer)
DEBUG [main] - < Total: 4
Book [id=1, name=深入理解Java虚拟机 JVM高级特性与最佳实践, press=机械工业出版社, author=周志明著, isbn=9787111421900, douban=8.8]
Book [id=2, name=疯狂Java讲义 第3版, press=电子工业出版社, author=李刚著, isbn=9787121236693, douban=7.8]
Book [id=4, name=Java编程思想 第4版, press=机械工业出版社, author=(美)Bruce Eckel著, isbn=9787111213826, douban=9.1]
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=9.9]
Book [id=1, name=深入理解Java虚拟机 JVM高级特性与最佳实践, press=机械工业出版社, author=周志明著, isbn=9787111421900, douban=8.8] //同一条语句,第二次查询,从缓存中拿数据,没有执行新的sql查询
Book [id=2, name=疯狂Java讲义 第3版, press=电子工业出版社, author=李刚著, isbn=9787121236693, douban=7.8]
Book [id=4, name=Java编程思想 第4版, press=机械工业出版社, author=(美)Bruce Eckel著, isbn=9787111213826, douban=9.1]
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=9.9]
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=? //其实要查询的数据在缓存已经存在,但是还是执行了一次查询
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=9.9]
测试结论之二:要最终执行相同的sql语句,才会到一级缓存中拿数据
MyBatis使用HashMap存储缓存信息,key根据查询出来的对象生成,value是查询出来的对象
使用一级缓存要特别注意
:缓存的作用范围是SqlSession,假如SqlSessionA中已经有了一级缓存,而SqlSessionB修改了数据库的数据,此时SqlSessionA再执行相同查询,会从SqlSessionA的一级缓存中拿数据,导致跟数据库中的不同。关于这一点,是否有别的配置可以解决,还没见到,或许要通过编程解决,看下面的测试代码:
package net.sonng.mbt.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.sonng.mbt.entity.Book;
import net.sonng.mbt.mapper.BookMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class BookTest {
public static void main(String[] args) throws IOException{
InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1=sqlSessionFactory.openSession();
BookMapper bookMapper1=session1.getMapper(BookMapper.class);
Book book1=bookMapper1.findBookById(5); //session1的第一次查询,入库查询
System.out.println(book1);
Book book2=bookMapper1.findBookById(5); //session1的第二次查询,从一级缓存中拿数据
System.out.println(book2);
SqlSession session2=sqlSessionFactory.openSession();
BookMapper bookMapper2=session2.getMapper(BookMapper.class);
Book book3=bookMapper2.findBookById(5); //session2的第一次查询,入库查询
book3.setDouban(10.0f);
bookMapper2.updateBook(book3);
session2.commit(); //session2修改了数据库中的数据,并且提交了事务
Book book4=bookMapper2.findBookById(5); //session2的第二次查询,入库查询
System.out.println(book4);
session2.commit();
Book book5=bookMapper1.findBookById(5); //session1的第三次查询,从一级缓存中拿到过时的数据
System.out.println(book5);
}
}
输出如下:
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=? //
session1第一次查询,执行sql,入库查询
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=6.9]
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=6.9] //session1第二次查询,从一级缓存中拿数据
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=? //session2第一次查询,执行sql,入库查询
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
DEBUG [main] - ==> Preparing: UPDATE book SETname
=?, press=?, author=?, isbn=?, douban=? WHERE id=5 //session2修改了数据库的数据,且提交了事务
DEBUG [main] - > Parameters: 深入理解Java 7 核心技术与最佳实践(String), 机械工业出版社(String), 成富著(String), 9787111380399(String), 10.0(Float)
DEBUG [main] - < Updates: 1
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=? //session2第二次查询,执行sql,入库查询
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=10.0] //由此可以看出,数据库中的数据已经被修改
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=6.9] //session1第三次查询,从一级缓存中拿到了过时的数据
测试结论之三:sessionA修改了数据库的数据,不会影响到sessionB的一级缓存数据
二级缓存
二级缓存是跨SqlSession的,其作用范围是同一个mapper映射文件,就是同一个命名空间。
MyBatis默认开启一级缓存,但没有开启二级缓存,因此还需要配置
在mybatis-config.xml的settings元素中开启二级缓存
<settings>
<setting name="logImpl" value="LOG4J" />
<setting name="cacheEnabled" value="true" />
</settings>
因为二级缓存是基于mapper映射文件的,因此映射文件中还要进行一些配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTDMapper 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="net.sonng.mbt.mapper.BookMapper">
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true" /> <!-- 二级缓存配置 -->
<select id="findAll" resultType="net.sonng.mbt.entity.Book">
SELECT * FROM book
</select>
<select id="findBookById" parameterType="int" resultType="net.sonng.mbt.entity.Book">
SELECT * FROM book WHERE id=#{id}
</select>
<select id="findBooks" parameterType="net.sonng.mbt.entity.Book" resultType="net.sonng.mbt.entity.Book">
SELECT * FROM book WHERE
<choose>
<when test="isbn!=null" >
isbn=#{isbn}
</when>
<when test="name!=null">
`name`=#{name}
</when>
<otherwise>
douban>7
</otherwise>
</choose>
</select>
<update id="updateBook" parameterType="net.sonng.mbt.entity.Book" >
UPDATE book
<set>
<if test="name!=null" >`name`=#{name},</if>
<if test="press!=null" >press=#{press},</if>
<if test="author!=null" >author=#{author},</if>
<if test="isbn!=null" >isbn=#{isbn},</if>
<if test="douban!=null" >douban=#{douban}</if>
</set>
WHERE id=${id}
</update>
<select id="findBookInId" parameterType="list" resultType="net.sonng.mbt.entity.Book">
SELECT * FROM book WHERE id In
<foreach item="id" index="i" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<select id="findBookLikeName" parameterType="string" resultType="net.sonng.mbt.entity.Book">
<bind name="pattern" value="'%'+_parameter+'%'" />
SELECT * FROM book WHERE `name` LIKE #{pattern}
</select>
</mapper>
cache元素的几个属性:
----flushInterval:刷新间隔,单位毫秒。默认不设置,即不刷新,仅在调用语句时刷新。
----size:缓存的数目,默认1024
----readOnly:是否只读,默认false。true表示多次查询时返回的是这个对象的引用;false表示返回的是对象的拷贝
----eviction:收回策略,默认LRU。
--------LRU:最近最少使用策略,移除长时间不被使用的对象
--------FIFO:先进先出策略,按对象进入缓存的顺序移除
--------SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象
--------WEAK:弱引用策略
实体类还要实现Serializable接口
二级缓存测试之一:
package net.sonng.mbt.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.sonng.mbt.entity.Book;
import net.sonng.mbt.mapper.BookMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class BookTest {
public static void main(String[] args) throws IOException{
InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1=sqlSessionFactory.openSession();
BookMapper bookMapper1=session1.getMapper(BookMapper.class);
Book book1=bookMapper1.findBookById(5); //第一次查询
System.out.println(book1);
session1.commit(); //session1提交事务,特别注意:如果这里不commit,那么没有二级缓存
SqlSession session2=sqlSessionFactory.openSession();
BookMapper bookMapper2=session2.getMapper(BookMapper.class);
Book book2=bookMapper2.findBookById(5); //第二次查询
System.out.println(book2);
session2.close();
session1.close();
}
}
输出如下:
DEBUG [main] - Cache Hit Ratio [net.sonng.mbt.mapper.BookMapper]: 0.0 //
session1第一次查询,二级缓存中没有数据
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=?
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=10.0]
DEBUG [main] - Cache Hit Ratio [net.sonng.mbt.mapper.BookMapper]: 0.5 //session2第一次查询,从二级缓存中命中数据
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=10.0]
测试结论之四:sessionA执行一次查询,如果提交了事务,查询的结果会写到二级缓存中,sessionB执行相同查询,会查询到二级缓存中的数据。注意要提交事务才会写到二级缓存中。
二级缓存测试之二:
package net.sonng.mbt.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.sonng.mbt.entity.Book;
import net.sonng.mbt.mapper.BookMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class BookTest {
public static void main(String[] args) throws IOException{
InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1=sqlSessionFactory.openSession();
BookMapper bookMapper1=session1.getMapper(BookMapper.class);
Book book1=bookMapper1.findBookById(5); //session1第一次查询
System.out.println(book1);
session1.commit();
book1.setDouban(10.0f);
bookMapper1.updateBook(book1); //session1修改了数据
session1.commit(); //标注一。
SqlSession session2=sqlSessionFactory.openSession();
BookMapper bookMapper2=session2.getMapper(BookMapper.class);
Book book2=bookMapper2.findBookById(5); //session2第一次查询
System.out.println(book2);
session2.close();
}
}
输出如下:
DEBUG [main] - Cache Hit Ratio [net.sonng.mbt.mapper.BookMapper]: 0.0
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=?
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=6.9]
DEBUG [main] - ==> Preparing: UPDATE book SETname
=?, press=?, author=?, isbn=?, douban=? WHERE id=5
DEBUG [main] - > Parameters: 深入理解Java 7 核心技术与最佳实践(String), 机械工业出版社(String), 成富著(String), 9787111380399(String), 10.0(Float)
DEBUG [main] - < Updates: 1
DEBUG [main] - Cache Hit Ratio [net.sonng.mbt.mapper.BookMapper]: 0.0 //session2的第一次查询,没有命中,需要入库查询,说明session1的update语句清除了二级缓存
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=?
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=10.0]
如果把测试代码二的标注一这行代码注释掉的话,也就是不提交事务,输出如下:
DEBUG [main] - Cache Hit Ratio [net.sonng.mbt.mapper.BookMapper]: 0.0
DEBUG [main] - ==> Preparing: SELECT * FROM book WHERE id=?
DEBUG [main] - > Parameters: 5(Integer)
DEBUG [main] - < Total: 1
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=6.9]
DEBUG [main] - ==> Preparing: UPDATE book SETname
=?, press=?, author=?, isbn=?, douban=? WHERE id=5
DEBUG [main] - > Parameters: 深入理解Java 7 核心技术与最佳实践(String), 机械工业出版社(String), 成富著(String), 9787111380399(String), 10.0(Float)
DEBUG [main] - < Updates: 1
DEBUG [main] - Cache Hit Ratio [net.sonng.mbt.mapper.BookMapper]: 0.5 //session2的第一次查询,命中了数据,二级缓存中还有数据
Book [id=5, name=深入理解Java 7 核心技术与最佳实践, press=机械工业出版社, author=成富著, isbn=9787111380399, douban=10.0]
再看数据库,数据并没有被修改,这里出现了脏读。没有提交事务,这里的update语句只修改了缓存中的数据,数据库中的数据并没有修改,另一个sqlSession脏读到了缓存中的数据。
测试结论之五:sessionA如果执行了DML操作且提交了事务,会清空二级缓存;如果只执行DML操作但不提交事务,那么会修改二级缓存中的数据,而不会实际修改数据库的数据,这时候会导致sessionA或其他session的脏读。
实际上只要该mapper命名空间下的sql执行了DML并提交事务,该命名空间的二级缓存都会清空。
小结
一级缓存默认开启,二级缓存默认不开启,需要到MyBatis-config.xml中开启
一级缓存的作用范围是SqlSession,二级缓存是跨SqlSession的,是mapper映射文件级别的
一级缓存不管是否提交事务,前面的查询结果都会写到一级缓存中;但二级缓存有区别,前面的查询提交了事务,才会把结果写到二级缓存中
一级缓存会在session执行了DML时清空
二级缓存会在命名空间下的DML语句被执行并提交了事务时清空
顺序:二级缓存、一级缓存、入库查询
其他:
CSDN 《深入理解mybatis原理》 MyBatis的二级缓存的设计原理
CSDN mybatis 一级缓存和二级缓存简介
0065 MyBatis一级缓存与二级缓存的更多相关文章
- [原创]关于mybatis中一级缓存和二级缓存的简单介绍
关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...
- MyBatis 延迟加载,一级缓存,二级缓存设置
什么是延迟加载 resultMap中的association和collection标签具有延迟加载的功能. 延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息.使用关联信息时再去加载关联信息 ...
- mybatis高级(3)_延迟加载_深度延迟_一级缓存_二级缓存
设置延迟加载需要在mybatis.xml中设置 注: 侵入式延迟加载为真时是延迟加载 侵入式延迟加载为假时是深度延迟加载 <!-- 延迟加载和深度延迟加载 --> <settings ...
- 9.Mybatis一级缓存和二级缓存
所谓的缓存呢?其实原理很简单,就是在保证你查询的数据是正确的情况下,没有去查数据库,而是直接查找的内存,这样做有利于缓解数据库的压力,提高数据库的性能,Mybatis中有提供一级缓存和二级缓存. 学习 ...
- 八 mybatis查询缓存(一级缓存,二级缓存)和ehcache整合
1 查询缓存 1.1 什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存.
- myBatis学习(9):一级缓存和二级缓存
正如大多数持久层框架一样,MyBatis同样提供了一级缓存和二级缓存的支持 1. MyBatis一级缓存基于PerpetualCache的HashMap本地缓存,其存储作用域为 Session,默认情 ...
- mybatis 详解(九)------ 一级缓存、二级缓存
上一章节,我们讲解了通过mybatis的懒加载来提高查询效率,那么除了懒加载,还有什么方法能提高查询效率呢?这就是我们本章讲的缓存. mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解 ...
- MyBatis从入门到放弃六:延迟加载、一级缓存、二级缓存
前言 使用ORM框架我们更多的是使用其查询功能,那么查询海量数据则又离不开性能,那么这篇中我们就看下mybatis高级应用之延迟加载.一级缓存.二级缓存.使用时需要注意延迟加载必须使用resultMa ...
- Mybatis第八篇【一级缓存、二级缓存、与ehcache整合】
Mybatis缓存 缓存的意义 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题. myba ...
随机推荐
- Tomcat与Servlet工作流程
什么是Tomcat? Tomcatserver是一个免费的开放源码的Web 应用server,属于轻量级应用server. 什么是Servlet? Servlet是在server上执行的小程序.,说白 ...
- BigDecimal 执行精确小数计算
来考虑这样一种情况,先来看代码: public class Test { public static void main(String[] args) { System.out.println(0.4 ...
- HTML:基本的标签
概述: <html></html>标准的语言格式,回环标签,有头和躯体部分,头里面一般显示标题title,躯体部分显示内容:背景色.文字.图片.超链接.表格.表单等. 可以直接 ...
- python2 python3编码问题记录
最近在服务器上跑脚本,linux自带的是python 2.x,中文显示经常有问题,通过下面两篇终于弄懂了. https://www.cnblogs.com/575dsj/p/7112767.html ...
- IntelliJ IDEA 插件开发视频教程
IntelliJ IDEA 插件开发视频教程 学习了:http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/plugins-develo ...
- win8.1安装开发工具 vs2015 Visual Studio 2015 Preview Downloads
1.首先全新安装win8.1 略 破解激活.... 2.安装Visual Studio 2015 Visual Studio 2015 是免费的,不存在破解版本, 如果安装过程中存在问题,建议先把本文 ...
- (笔试题)N!的三进制数尾部0的个数
题目: 用十进制计算30!(30的阶乘),将结果转换成3进制进行表示的话,该进制下的结果末尾会有____个0. 思路: 这道题与上一篇博文N!尾部连续0的个数的思路是一样的. 计算N!下三进制结果末尾 ...
- SCU 4313 把一棵树切成每段K个点 (n%k)剩下的点不管
题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=4313 判断是不是存在拆图得到新连通分支的点个数是K的倍数 注意一个点所连的边只能被切一条 # ...
- vue常用属性解释。
props:详看 示例-网格组件. props 可以是数组或对象,用于接收来自父组件的数据.props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测.自定义校验和设置默认值 ...
- 查找文件工具find
与locate.whereis命令相比,find具有本质的区别: 首先,find是从指定的位置进行遍历查找(可以理解为对文件和目录进行逐一查找). 其次,find可以查找具有某一类特征的文件(例如查找 ...