Mybatis的一级缓存:
MyBatis的一级缓存指的是在一个Session域内,session关闭前执行的查询会根据SQL为key被缓存(跟mysql缓存一样,修改任何参数的值都会导致缓存失效),用来保存用户对数据库的操作信息(sql)和数据库返回的数据,如果下一次用户再执行相同的请求,那么直接从内存中读数数据而不是从数据库读取。
Mybatis的一级缓存的作用域是在同一个SqlSession中,而且一级缓存在spring中是没有作用的;‘
单独使用MyBatis而不继承Spring,使用原生的MyBatis的SqlSessionFactory来构造sqlSession查询
public class Test {
public static void main(String[] args) throws IOException {
String mybatisconfig= "mybatisconfig.xml";
InputStream is = Resources.getResourceAsStream(config);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
System.out.println(sqlSession .selectOne("selectUserByID", 1));
// 同一个session的相同sql查询,将会使用一级缓存
System.out.println(sqlSession .selectOne("selectUserByID", 1));
// 参数改变,需要重新查询
System.out.println(sqlSession .selectOne("selectUserByID", 2));
// 清空缓存后需要重新查询
sqlSession .clearCache();
System.out.println(sqlSession .selectOne("selectUserByID", 1));
// sqlSession close以后,仍然使用同一个db connection
sqlSession .close();
sqlSession = factory.openSession();
System.out.println(sqlSession .selectOne("selectUserByID", 1));
}
}
执行后:当参数不变的时候只进行了一次查询,参数变更以后,则需要重新进行查询,而清空缓存以后,参数相同的查询过的SQL也需要重新查询,而且使用的数据库连接是同一个数据库连接,这里要得益于我们在mybatis config.xml里面的datasource设置
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatistest?characterEncoding=utf8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
注意:datasource使用的是POOLED,也就是使用了连接池,所以数据库连接可回收利用,当然这个environment属性在集成spring的时候是不需要的,因为我们需要另外配置datasource的bean.
跟Spring集成的时候(使用mybatis-spring),直接在dao里查询两次同样参数的sql
@Repository
public class UserDao extends SqlSessionDaoSupport {
public User selectUserById(int id) {
SqlSession session = getSqlSession();
session.selectOne("dao.userdao.selectUserByID", id);
// 由于session的实现是SqlSessionTemplate的动态代理实现
// 它已经在代理类内执行了session.close(),所以无需手动关闭session
return session.selectOne("dao.userdao.selectUserByID", id);
}
}
这里执行了2次sql查询,看似我们使用了同一个sqlSession,但是实际上因为我们的dao继承了SqlSessionDaoSupport,而SqlSessionDaoSupport内部sqlSession的实现是使用用动态代理实现的,这个动态代理sqlSessionProxy使用一个模板方法封装了select()等操作,每一次select()查询都会自动先执行openSession(),执行完close()以后调用close()方法,相当于生成了一个新的session实例,所以我们无需手动的去关闭这个session(),当然也无法使用mybatis的一级缓存,也就是说mybatis的一级缓存在spring中是没有作用的,所以一旦系统 采用了SSM的框架时一级缓存是无法使用的,只能使用二级缓存。
其中数据的生命周期有两个影响因素
1.commit操作
如果对sqlsession执行commit操作,也就意味着用户执行了update、delete等操作,那么数据库中的数据势必会发生变化,如果用户请求数据仍然使用之前内存中的数据,那么将读到脏数据。所以在执行sqlsession操作后,会清除保存数据的HashMap,用户在发起查询请求时就会重新读取数据并放入一级缓存中了。所以在commit之后mybatis对数据重新进行了查询。
2.关闭session
关闭sqlsession时,一般在mybatis集成spring时,会把SqlSessionFactory设置为单例注入到IOC容器中,不把sqlsession也设置为单例的原因是sqlsession是线程不安全的,所以不能为单例。那也就意味着其实是有关闭sqlsession的过程的。其实,对于每一个service中的sqlsession是不同的,这是通过mybatis-spring中的org.mybatis.spring.mapper.MapperScannerConfigurer创建sqlsession自动注入到service中的。
在第一次查询完后关闭sqlsession,然后创建新的sqlsession和mapper来重新执行一次查询操作说明关闭了sqlsession后的确把之前的缓存数据清空了,之后再执行同样的查询操作也会再访问一遍数据库。为了解决这个问题,需要使用二级缓存
而一级缓存的设计是每个sqlsession单独使用一个缓存空间,不同的sqlsession是不能互相访问数据的。当然,在sqlsession关闭后,其中数据自然被清空。
Mybatis的二级缓存:
二级缓存就是global caching,它超出session范围之外,可以被所有sqlSession共享,它的实现机制和mysql的缓存一样,开启它只需要在mybatis的配置文件开启settings里的.
需要注意的是global caching的作用域是针对Mapper的Namespace而言的,也就是说只在有在这个Namespace内的查询才能共享这个cache.例如上面的 dao.userdao namespace 。但并不是意味着同一个namespace创建的mapper可以互相读取缓存内容,
这里的原则是,如果开启了二级缓存,那么在关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。
开启二级缓存第一步:
打开二级缓存总开关:
打开总开关,只需要在mybatis总配置文件中加入一行设置
<setting name="cacheEnabled" value="true"/>
开启二级缓存第二步:
打开需要使用二级缓存的mapper的开关:
在需要开启二级缓存的mapper.xml中加入caceh标签
在UserMapper.xml中配置
<!--开启mybatis的二级缓存 -->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
开启二级缓存第三步:
让需要使用二级缓存的POJO类实现Serializable接口,如
public class User implements Serializable {