11.4 注解使用二级缓存

11.4 注解使用二级缓存

项目结构

展开/折叠
G:\workspace_web2\MyATwoLevelCacheTest
├─src\
│ ├─db.properties
│ ├─domain\
│ │ └─User.java
│ ├─fractory\
│ │ └─SqlSessionFactoryTools.java
│ ├─log4j.xml
│ ├─mapper\
│ │ └─UserMapper.java
│ ├─mybatis-config.xml
│ ├─tb_user.sql
│ └─test\
│   ├─DeleteUserTest.java
│   └─SelectTest.java
└─WebContent\
  ├─META-INF\
  │ └─MANIFEST.MF
  └─WEB-INF\
    └─lib\
      ├─commons-logging-1.2.jar
      ├─log4j-1.2.17.jar
      ├─log4j-api-2.3.jar
      ├─log4j-core-2.3.jar
      ├─mybatis-3.4.5.jar
      └─mysql-connector-java-5.1.44-bin.jar

数据库表

/MyATwoLevelCacheTest/src/tb_user.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#创建数据库表
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(18) DEFAULT NULL,
`sex` char(2) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
#插入数据
INSERT INTO `tb_user` VALUES ('1', '小明', '男', '21');
INSERT INTO `tb_user` VALUES ('2', '小王', '男', '22');
INSERT INTO `tb_user` VALUES ('3', '小丽', '女', '18');
INSERT INTO `tb_user` VALUES ('4', '小芳', '女', '18');
INSERT INTO `tb_user` VALUES ('5', '小王', '男', '22');

mybatis相关配置

db.properties

/MyATwoLevelCacheTest/src/db.properties
1
2
3
4
5
6
7
8
9
#保存为db.properties文件,然后在mybatis-config.xml中通过下面标签引入:
#<properties resource="db.properties"/>
#下面的标签放在mybatis-config.xml文件的environments标签的environment子标签的子标签dataSource标签中
#<property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/>

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=root

log4j.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration
PUBLIC "-//LOG4J//DTD LOG4J//EN"
"https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd" >
<!-- 请在mybatis-config.xml中配置如下设置 -->
<!-- <settings> -->
<!-- <setting -->
<!-- name="logImpl" -->
<!-- value="log4j"/> -->
<!-- </settings> -->
<log4j:configuration>
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%5p [%t] %m%n" />
</layout>
</appender>
<logger name="mapper.UserMapper">
<level value="DEBUG" />
</logger>
<root>
<level value="ERROR" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>

mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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">
<!-- 该配置文件包含对 MyBatis 系统的核心设置 -->
<configuration>
<properties resource="db.properties" />
<settings>
<setting name="logImpl" value="log4j" />
</settings>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="pooled">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="mapper.UserMapper" />
</mappers>
</configuration>

持久化对象

/MyATwoLevelCacheTest/src/domain/User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package domain;
import java.io.Serializable;
public class User
implements Serializable
{
// 使用缓存需要可序列话
private static final long serialVersionUID = 2667279148834038605L;
private Integer id;
private String name;
private String sex;
private Integer age;
public User()
{}
// 此处省略getter和setter方法,请自己补上
@Override
public String toString()
{
return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}

mapper接口

/MyATwoLevelCacheTest/src/mapper/UserMapper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package mapper;

import java.util.List;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.cache.decorators.LruCache;
import domain.User;

@CacheNamespace(
eviction = LruCache.class,
flushInterval = 60000,
size = 512,
readWrite = true
)
public interface UserMapper {
/**
* 根据id查询用户信息.
*
* @param id
* @return 用户记录
*/
@Select("select * from tb_user where id=#{id}")
@Options(useCache = true)
User selectUserById(Integer id);

/**
* 查询所有的用户信息.
*
* @return 用户列表
*/
@Select("select * from tb_user")
@Options(useCache = true)
List<User> selectAllUsers();

/**
* 根据id删除用户信息.
*
* @param id 主键
*/
@Delete("delete from tb_user where id=#{id}")
void deleteUserById(Integer id);
}

UserMapper接口中只是将之前写在XML文件当中的二级缓存配置写在了注解当中,其他并无不同。

@CacheNamespace注解

@CacheNamespace注解用来配置二级缓存,如下所示:

1
2
3
4
5
6
@CacheNamespace(
eviction = LruCache.class,
flushInterval = 60000,
size = 512,
readWrite = true
)
  • eviction属性表示要使用的回收策略的class,所有回收策略的类型都位于org.apache.ibatis.cache.decorators包下。
  • flushInterval表示刷新时间间隔,单位为毫秒
  • size表示缓存数目
  • readWrite=true表示只读

SqlSession工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package fractory;

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;

public class SqlSessionFactoryTools {
private static SqlSessionFactory sqlSessionFactory = null;
static
{
try
{
InputStream mybatisConfigXML = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(mybatisConfigXML);

} catch (IOException e)
{
e.printStackTrace();
}
}
public static SqlSession getSqlSession()
{
return sqlSessionFactory.openSession();
}
}

测试类

按id查询SelectTest.java

/MyATwoLevelCacheTest/src/test/SelectTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package test;
import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFratoryTools;
import mapper.UserMapper;
public class SelectTest{
public static void main(String[] args)
{
SqlSession sqlSession = null;
try
{
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUserById(1);
System.out.println(user);
// 关闭一级缓存
sqlSession.close();
// 重新获取会话
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 重新获取mapper接口代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println("-----------------------------------------------");
user = userMapper.selectUserById(1);
System.out.println(user);
// 提交事务
sqlSession.commit();
} catch (Exception e)
{
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally
{
// 关闭会话
if(sqlSession != null)
sqlSession.close();
}
}
}

运行效果:

1
2
3
4
5
6
7
8
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user where id=?
DEBUG [main] ==> Parameters: 1(Integer)
DEBUG [main] <== Total: 1
User [id=1, name=小明, sex=男, age=21]
-----------------------------------------------
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.5
User [id=1, name=小明, sex=男, age=21]

在关闭第一个sqlSession时,查询的信息会保存到二级缓存中,重新获取sqlSession,再次执行相同的查询时,由于二级缓存中已经有该数据,所以不需要再次执行sql语句,而是直接从二级缓存中返回数据.

按id删除DeleteUserTest.java

/MyATwoLevelCacheTest/src/test/DeleteUserTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package test;

import java.util.List;
import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFactoryTools;
import mapper.UserMapper;

public class DeleteUserTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAllUsers();
users.forEach(user -> System.out.println(" " + user));
System.out.println("----------------------------------------");
// userMapper.deleteUserById(users.get(0).getId());
// sqlSession.commit();
sqlSession.close();
System.out.println("----------------------------------------");
// 再次加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 再次获取mapper接口代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
users = userMapper.selectAllUsers();
users.forEach(user -> System.out.println(" " + user));
// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally {
// 关闭会话
if (sqlSession != null)
sqlSession.close();
}

}
}

运行效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user
DEBUG [main] ==> Parameters:
DEBUG [main] <== Total: 5
User [id=1, name=小明, sex=男, age=21]
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]
----------------------------------------
----------------------------------------
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.5
User [id=1, name=小明, sex=男, age=21]
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]

测试并没有执行删除语句,第二次查询时从二级缓存从返回数据.
取消删除语句的注释:

// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAllUsers();
users.forEach(user-> System.out.println(user));
System.out.println("----------------------------------------");
userMapper.deleteUserById(users.get(0).getId());
sqlSession.commit();
sqlSession.close();
System.out.println("----------------------------------------");
// 再次加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 再次获取mapper接口代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
users = userMapper.selectAllUsers();
users.forEach(user-> System.out.println(user));

再次执行,运行效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user
DEBUG [main] ==> Parameters:
DEBUG [main] <== Total: 5
User [id=1, name=小明, sex=男, age=21]
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]
----------------------------------------
DEBUG [main] ==> Preparing: delete from tb_user where id=?
DEBUG [main] ==> Parameters: 1(Integer)
DEBUG [main] <== Updates: 1
----------------------------------------
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user
DEBUG [main] ==> Parameters:
DEBUG [main] <== Total: 4
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]

DML语句会清空缓存

DML语句(insert,update,delete)会清空缓存,所以第二次查询时缓存中没有信息,需要再次执行SQL语句从数据库中查询信息.