mybatis的缓存机制是怎么样的?

正如大多数持久层框架一样,MyBatis 同样提供了 一级缓存 二级缓存 的支持。

1.缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。


mybatis的一级缓存作用就是在同一个session过程中,将会对相同数据的存取不通过数据库,而是通过缓存机制提高效率,看测试代码
<img src="https://pic3.zhimg.com/50/c225e22b0d00f5903b3626009228767e_hd.jpg" data-rawwidth="307" data-rawheight="347" class="content_image" width="307">
import java.io.IOException;
import java.io.InputStream;

import junit.framework.TestCase;

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 org.junit.Test;

import com.guowuxin.mybatis.mapper.UserMapper;
import com.guowuxin.mybatis.model.User;


/*用户表
drop table if exists user;
create table user(
    id int primary key auto_increment,
    username varchar(50) unique,
    password varchar(100),
    useraddress varchar(50)
);
insert into user values(1,"guowuxin","guowuxin","beijing");
insert into user values(1,"guoxiaoming","guowuxin","beijing");
*/

//如果两次查询都是查询1 将只会发送一条sql语句到数据库,否则将是2条sql语句

public class TestClass extends TestCase{
	public static SqlSessionFactory sqlSessionFactoy;
	
	static{
		String resource = "mybatis-config.xml";
		try {
			InputStream inputStream = Resources.getResourceAsStream(resource);
			sqlSessionFactoy = new SqlSessionFactoryBuilder().build(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Test 
    public void testgetUserById() { 
       SqlSession sqlSession = sqlSessionFactoy.openSession(); 
       try { 
                 UserMapper mapper = sqlSession.getMapper(UserMapper.class); 

                 User u1=mapper.getUserById(1);
                 System.out.println(u1);
                 User u2=mapper.getUserById(1);
                 System.out.println(u2);
                 
                 sqlSession.commit();
       } finally { 
           sqlSession.close(); 
       } 
    }
}
package com.guowuxin.mybatis.mapper;

import com.guowuxin.mybatis.model.User;

//通过面向接口的mybatis编程方式,需要保证方法名和配置文件中的id名称一致
public interface UserMapper {
    public User getUserById(int id);
}
package com.guowuxin.mybatis.model;
 
public class User {
    private int id;
    private int age;
    private String userName;
    private String userAddress;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString()
    {
    	final StringBuilder builder = new StringBuilder();
    	builder.append("id=").append(id).append(",");
		builder.append("age=").append(age).append(",");
		builder.append("userName=").append(userName).append(",");
		builder.append("userAddress=").append(userAddress).append(".");

		return builder.toString();
    	
    }
}
log4j.rootLogger=DEBUG,Console
ˆ†
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d %t %-5p (%c) - %m%n


log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeAliases>
		<typeAlias alias="User" type="com.guowuxin.mybatis.model.User" />
	</typeAliases>
	<environments default="development">
	    <environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">  
	                <property name="driver" value="com.mysql.jdbc.Driver" />
				    <property name="url" value="jdbc:mysql://127.0.0.1:3306/guowuxin" />
				    <property name="username" value="root" />
				    <property name="password" value="admin" />
	        </dataSource>
		</environment>
	</environments>

	<mappers>
	     <mapper resource="UserMapper.xml" />
	</mappers>

</configuration>
<?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"> 
<!-- 这里namespace必须是PostsMapper接口的路径,不然要运行的时候要报错 “is not known to the MapperRegistry”--> 
<mapper namespace="com.guowuxin.mybatis.mapper.UserMapper"> 
    <!-- 这儿的resultType是配置在mybatis-config.xml中得别名 -->
    <select id="getUserById" parameterType="int" resultType="User">
            select * from user where id=#{id}
    </select>
</mapper>

结果:
....................
2016-02-24 20:12:37,568 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:12:37,568 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>  Preparing: select * from user where id=? 
2016-02-24 20:12:37,586 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)
id=1,age=0,userName=guowuxin,userAddress=beijing.
id=1,age=0,userName=guowuxin,userAddress=beijing.
2016-02-24 20:12:37,593 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:12:37,593 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:12:37,593 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052 to pool.

如果取得2个不同的id
.......................
2016-02-24 20:12:57,655 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 3874052.
2016-02-24 20:12:57,656 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:12:57,656 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>  Preparing: select * from user where id=? 
2016-02-24 20:12:57,664 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)
id=1,age=0,userName=guowuxin,userAddress=beijing.
2016-02-24 20:12:57,679 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:12:57,679 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>  Preparing: select * from user where id=? 
2016-02-24 20:12:57,679 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 2(Integer)
id=2,age=0,userName=guoxiaoming,userAddress=beijing.
2016-02-24 20:12:57,679 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:12:57,679 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:12:57,679 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052 to pool.



二级缓存机制指的则是在不同的session之间都可以共享相同的数据内容。
import java.io.IOException;
import java.io.InputStream;

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 org.junit.Test;

import com.guowuxin.mybatis.mapper.UserMapper;
import com.guowuxin.mybatis.model.User;

import junit.framework.TestCase;


public class TestTwoCache extends TestCase{
    public static SqlSessionFactory sqlSessionFactory;
	
	static{
		String resource = "mybatis-config.xml";
		try {
			InputStream inputStream = Resources.getResourceAsStream(resource);
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Test 
    public void testCache() { 
       SqlSession sqlSession1 = sqlSessionFactory.openSession();
       
       SqlSession sqlSession2 = sqlSessionFactory.openSession();
       try { 
                 UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class); 
                 User u1=mapper1.getUserById(1);
                 System.out.println(u1);
                 sqlSession1.commit();
                 
                 UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
                 User u2=mapper2.getUserById(1);
                 System.out.println(u2);
                 
                 sqlSession2.commit();
       } finally { 
           sqlSession1.close(); 
           sqlSession2.close();
       } 
    }
}

在没有开启二级缓存之前,将会执行两次sql语句。
2016-02-24 20:28:10,786 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - PooledDataSource forcefully closed/removed all connections.
2016-02-24 20:28:10,850 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Openning JDBC Connection
2016-02-24 20:28:11,011 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 3874052.
2016-02-24 20:28:11,012 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:28:11,013 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=?
2016-02-24 20:28:11,030 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)
id=1,age=0,userName=guowuxin,userAddress=beijing.
2016-02-24 20:28:11,040 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Openning JDBC Connection
2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 29420695.
2016-02-24 20:28:11,154 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]
2016-02-24 20:28:11,154 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=?
2016-02-24 20:28:11,154 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)
id=1,age=0,userName=guowuxin,userAddress=beijing.
2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]
2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052 to pool.
2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]
2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]
2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 29420695 to pool.

------------------------
在实体类映射文件中加入<cache/>
<?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"> 
<!-- 这里namespace必须是PostsMapper接口的路径,不然要运行的时候要报错 “is not known to the MapperRegistry”--> 
<mapper namespace="com.guowuxin.mybatis.mapper.UserMapper"> 
    <!-- 这儿的resultType是配置在mybatis-config.xml中得别名 -->
     <cache />
    <select id="getUserById" parameterType="int" resultType="User">
            select * from user where id=#{id}
    </select>
</mapper>

2016-02-25 10:15:11,162 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 22522451.
2016-02-25 10:15:11,164 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@157aa53]
2016-02-25 10:15:11,164 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>  Preparing: select * from user where id=? 
2016-02-25 10:15:11,182 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)
id=1,age=0,userName=guowuxin,userAddress=beijing.
2016-02-25 10:15:11,196 main DEBUG (org.apache.ibatis.cache.decorators.LoggingCache) - Cache Hit Ratio [com.guowuxin.mybatis.mapper.UserMapper]: 0.5
id=1,age=0,userName=guowuxin,userAddress=beijing.
2016-02-25 10:15:11,196 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@157aa53]
2016-02-25 10:15:11,196 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@157aa53]
2016-02-25 10:15:11,197 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 22522451 to pool.

就会发现cache命中,只执行了一次sql语句

------------------------------
如果二级缓存想要命中实现,则必须要将上一次sqlSession commit之后才能生效,不然将不会命中,原因:
两个不同的session必须提交前面一个session才能缓存生效的原因是因为mybatis的缓存会被一个transactioncache类包装住,所有的cache.putObject全部都会被暂时存到一个map里,等事务提交以后,这个map里的缓存对象才会被真正的cache类执行putObject操作。

这么设计的原因是为了防止事务执行过程中出异常导致回滚,如果get到object后直接put进缓存,万一发生回滚,就很容易导致mybatis缓存被脏读
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值