1.Mybatis的底层工作原理
此详细过程请看前博客 “Mybatis的工作原理”
2.测试缓存的环境准备
利用idea+maven+junit搭建一个项目,如果此步骤还不熟
请参照IDEA 之maven+junit构建。
然加入jdbc依赖 ,log4j2所需的依赖:
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle/ojdbc14 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0.3.0</version>
</dependency>
<!--添加log4j2依赖-->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-web -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.8.2</version>
</dependency>
要看到mybatis的缓存就必须,通过日志来跟踪sql,所以还要整合log4j2。
log4j2的配置:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!--输出的目的地-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l %msg%n"/>
</Console>
</Appenders>
<Loggers>
<!--其他的日志以 error 为级别 ,除非有logger 覆盖 如下-->
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
<!-- mapper结构所在的包-->
<Logger name="com.worldly.config.mapper" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
</Loggers>
</Configuration>
用junit测试selectEmployeeList可以看到如下console输出,就说明环境已经准备好了。
3.Mybatis的缓存
3.1 mybatis的一级缓存SqlSession缓存
sqlSession缓存是默认开启的,在一个sqlSession在使用select的时候会先从本地缓存中读,如果没有才会连接数据库查询相关的数据,然后将查询的数据放入本地缓存,以便下次select。只要事务不提交( 增删该等动作)或者没有sqlSession.close(), 执行查询条件相同的第二次查询,就直接从缓存中取,不发sql。
@Test
public void testCache1() {
InputStream resourceAsStream = ConfigTest.class.getResourceAsStream("../classes/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//查询id为1的员工
Employee param = new Employee();
param.setId(1);
/**
* 1用sqlSession 获取mapper的代理对象 执行按条件查询,
* 由于是第一次查询,缓存中没有,会发出查询sql
*/
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.selectEmployeeByCondition(param);
System.out.println(employee);
/**
* 用同一sqlSession 获取mapper的代理对象 执行与1相同的查询
* 1查询后缓存中有数据,如果是相同的查询就直接从缓存中取,不发sql语句
*/
EmployeeMapper mapper2 = sqlSession.getMapper(EmployeeMapper.class);
Employee employee2 = mapper2.selectEmployeeByCondition(param);
System.out.println(employee2);
//触发事务commit
sqlSession.commit();
/**
* 事务提交。本地缓存清空
* 虽然同sqlSession 执行为条件相同的查询,但还是要connection db 进行select
*/
EmployeeMapper mapper3 = sqlSession.getMapper(EmployeeMapper.class);
Employee employee3 = mapper3.selectEmployeeByCondition(param);
System.out.println(employee3);
}
3.2 mybatis的二级缓存Mapper级别的缓存
mybatis的二级缓存是namespace级别的,就是同一Mapper下的多个sqlSession共享一个缓存。因此只要有一个sqlSession发出了一个查询,第二sqlSession发出同样的查询的话,是直接从二级缓存中取数据的。
二级缓存默认不开启,如果要使用的话要在mybatis-config.xml的全局配置文件中开启,同时要在mapper 的sql映射文件中配置cache, 此外缓存的对象 必须 implments Serializable接口。
<!--配置缓存的全局开关 如果要使用缓存 就必须设置-->
<setting name="cacheEnabled" value="true"/>
<!--mapper映射文件中的开启-->
<?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.worldly.config.mapper.EmployeeMapper">
<!--开启缓存,如果不设置就默认使用mybatis自身的二级缓存-->
<cache/>
<resultMap id="empResultMap" type="com.worldly.config.entity.Employee">
<id column="emp_id" property="id"></id>
<result column="emp_name" property="name"/>
<result column="emp_email" property="email"/>
<result column="emp_tel" property="tel"/>
<result column="emp_dep" property="depId"/>
</resultMap>
<select id="selectEmployeeList" resultMap="empResultMap" databaseId="mysql">
select * from t_emp
</select>
<select id="selectEmployeeByCondition" resultMap="empResultMap" databaseId="mysql">
select * from t_emp WHERE emp_id=#{param.id}
</select>
</mapper>
我们用两个sqlSession去查询同一sql, 看是否会发出sql
测试代码:
@Test
public void testCache2() {
InputStream resourceAsStream = ConfigTest.class.getResourceAsStream("../classes/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//查询id为1的员工
Employee param = new Employee();
param.setId(1);
/**
* 1用sqlSession 获取mapper的代理对象 执行按条件查询,
* 由于是第一次查询,缓存中没有,会发出查询sql
*/
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.selectEmployeeByCondition(param);
System.out.println(employee);
//触发事务commit
sqlSession.commit();
/**
* 用同一sqlSession 获取mapper的代理对象 执行与1相同的查询
* 1查询后缓存中有数据,如果是相同的查询就直接从缓存中取,不发sql语句
*/
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);
Employee employee2 = mapper2.selectEmployeeByCondition(param);
System.out.println(employee2);
}