Spring缓存管理Demo:实现对用户信息CURD的缓存管理,上述理论的确枯燥无味,而且还容易让人头大,看了很多博客和分析,搭建一个测试Demo吧,好记性不如烂笔头。

1. User数据表创建
2. 基础SpringBoot项目环境搭建
3. Dao层
4. Service层
5. Controller层
6. 测试结果

1.数据表创建

CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `uuid` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

2.基础SpringBoot项目环境搭建

在IDEA中创建SpringBoot项目很简单,包括依赖的导入。
File - New Project - Springinitializr - default - https://start.spring.io/
配置项目中的相关参数,采用maven构建项目,可勾选依赖,根据个人需要,注意弹出框右上角有选择Springboot版本的按钮,默认2.0+,改成1.5即可。
Springboot2.0和Springboot1.0有些许差距,建议选择Springboot1.0版本
基础项目创建完成之后,需要添加cache方面的相关依赖。
<!--开启 cache 缓存-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- ehcache 缓存 -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>
<!--log4j2日志-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--Druid数据连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.20</version>
</dependency>
在resources目录下编辑application.properties文件:
# 数据库驱动配置信息
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = 1234
spring.datasource.driverClassName = com.mysql.jdbc.Driver

# Druid连接池的配置信息
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

# Log4j2 配置
logging.config=classpath:log4j2.xml

resources添加配置文件:
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--启动项设置为 trace,加载 springboot 启动内部各种详细输出-->
<Configuration status="trace">
    <Appenders>
        <!--添加一个控制台追加器-->
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout>
                <pattern>[%-5p] %d %c - %m%n</pattern>
            </PatternLayout>
        </Console>
        <!--添加一个文本追加器,文件位于根目录下,名为log.log-->
        <File name="File" fileName="log.log">
            <PatternLayout>
                <pattern>[%-5p] %d %c - %m%n</pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
        <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
        <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
        <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
        <Logger name="org.springframework" level="warn" />
        <Logger name="com.github" level="debug" />
        <!--记录 qg.fangrui.boot 包及其子包 debug 及其以上的记录,并输出到文件中-->
        <Logger name="qg.fangrui.boot" level="debug">
            <!-- AppenderRef 可以控制文件输出对象-->
            <AppenderRef ref="File" />
        </Logger>
        <!--根记录全部输出到控制台上-->
        <Root level="debug">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <defaultCache
            eternal="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />

    <!-- 自定义缓存策略 通过name进行缓存策略的区分 这里的缓存策略名为users-->
    <cache
            name="users"
            eternal="false"
            maxElementsInMemory="100"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="300"
            memoryStoreEvictionPolicy="LRU" />
</ehcache>

启动主类:Application.java添加@EnableCaching缓存开启注解

3.Dao层

mybatis3.0+后可以通过注解替代mapper文件。
@Mapper
public interface UserDao {

    @Select("select * from users where uuid = #{uuid}")
    User getUserByUuid(String uuid);

    @Insert("insert into users (uuid, name, age) values (#{uuid}, #{name}, #{age})")
    int addUser(User user);

    @Update("update users set uuid = #{uuid}, name = #{name}, age = #{age} where uuid = #{uuid}")
    int updateUser(User user);

    @Delete("delete from users where uuid = #{uuid}")
    void deleteUser(String uuid);
}

4.Service层

service层采用了接口+实现类的方式进行处理。缓存管理系列注解上述已经说明。
@Service
public class UserServiceImpl implements UserService{

    //对应ehcache.xml中的缓存策略名称
    private static final String CACHE_NAME = "users";

    @Autowired
    private UserDao userDao;

    @Override
    public int addUser(User user) {
        return userDao.addUser(user);
    }

    @CacheEvict(value = CACHE_NAME, key = "'user_' + #uuid")
    @Override
    public void deleteUser(String uuid) {
        userDao.deleteUser(uuid);
    }

    @Cacheable(value = CACHE_NAME, key = "'user_' + #uuid")
    @Override
    public User getUserByUuid(String uuid) {
        System.out.println("此处查询了数据库:用户UUID为" + uuid);
        return userDao.getUserByUuid(uuid);
    }


    @CacheEvict(value = CACHE_NAME, key = "'user_' + #user.getUuid()")
    @Override
    public int updateUser(User user) throws Exception {
        User temp = userDao.getUserByUuid(user.getUuid());
        if (null == temp) {
            throw new Exception("未找到待修改对象");
        }
        temp.setName(user.getName());
        temp.setAge(user.getAge());
        return userDao.updateUser(temp);
    }
}

5.Controller层:这里的添加、修改、删除后分别进行了三次数据查询。

@Controller
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    //定义全局测试用户
    User user = new User(UUID.randomUUID().toString(), "张三", 18);

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/ehcache")
    @ResponseBody
    public String ehcache() throws Exception {
        System.out.println("构建测试用户");

        if (userService.addUser(user) == 0) {
            logger.debug("新建用户失败");
            throw new Exception();
        }
        logger.debug("新建用户成功");
        System.out.println(userService.getUserByUuid(user.getUuid()));
        System.out.println(userService.getUserByUuid(user.getUuid()));
        System.out.println(userService.getUserByUuid(user.getUuid()));

        System.out.println("测试修改");
        user.setName("张三改");
        user.setAge(20);
        if (userService.updateUser(user) == 0) {
            logger.debug("修改用户失败");
            throw new Exception();
        }
        logger.debug("修改用户成功");
        System.out.println(userService.getUserByUuid(user.getUuid()));
        System.out.println(userService.getUserByUuid(user.getUuid()));
        System.out.println(userService.getUserByUuid(user.getUuid()));


        System.out.println("测试删除");
        userService.deleteUser(user.getUuid());
        System.out.println(userService.getUserByUuid(user.getUuid()));
        System.out.println(userService.getUserByUuid(user.getUuid()));
        System.out.println(userService.getUserByUuid(user.getUuid()));
        return "测试完毕";
    }
}

6.测试结果

启动项目,测试请求:localhost:8080/ehcache
观察控制台打印的结果数据:
1. 构建测试用户
[DEBUG] 2018-08-09 11:39:28,553 org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
[DEBUG] 2018-08-09 11:39:28,556 org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4be30c72] was not registered for synchronization because synchronization is not active
[INFO ] 2018-08-09 11:39:28,585 com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
[DEBUG] 2018-08-09 11:39:28,708 org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@4b6ca547] will not be managed by Spring
[DEBUG] 2018-08-09 11:39:28,711 com.jyf.ex.ehcache.dao.UserDao.addUser - ==>  Preparing: insert into users (uuid, name, age) values (?, ?, ?) 
[DEBUG] 2018-08-09 11:39:28,723 com.jyf.ex.ehcache.dao.UserDao.addUser - ==> Parameters: 6e790f91-e5ab-4a26-8b49-17d2816cd6ce(String), 张三(String), 18(Integer)
[DEBUG] 2018-08-09 11:39:28,725 com.jyf.ex.ehcache.dao.UserDao.addUser - <==    Updates: 1
[DEBUG] 2018-08-09 11:39:28,726 org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4be30c72]
[DEBUG] 2018-08-09 11:39:28,726 com.jyf.ex.ehcache.controller.UserController - 新建用户成功
2. 添加后查询:
此处查询了数据库:用户UUID为84bebbe5-b870-446f-96cc-39277e869aec
[DEBUG] 2018-08-09 12:47:28,638 org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
[DEBUG] 2018-08-09 12:47:28,638 org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2b1e955f] was not registered for synchronization because synchronization is not active
[DEBUG] 2018-08-09 12:47:28,639 org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@755d3205] will not be managed by Spring
[DEBUG] 2018-08-09 12:47:28,639 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - ==>  Preparing: select * from users where uuid = ? 
[DEBUG] 2018-08-09 12:47:28,639 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - ==> Parameters: 84bebbe5-b870-446f-96cc-39277e869aec(String)
[DEBUG] 2018-08-09 12:47:28,647 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - <==      Total: 1
[DEBUG] 2018-08-09 12:47:28,648 org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2b1e955f]
User{id=6, uuid='84bebbe5-b870-446f-96cc-39277e869aec', name='张三', age=18} //查询数据库并加入缓存
User{id=6, uuid='84bebbe5-b870-446f-96cc-39277e869aec', name='张三', age=18} //查询缓存
User{id=6, uuid='84bebbe5-b870-446f-96cc-39277e869aec', name='张三', age=18} //查询缓存
由于在数据查询service层业务逻辑中加入了打印标识。这里只出现了一条标识,说明访问数据库一次后,将查询结果放入缓存,第二三此查询均从缓存中进行查询。
3. 修改后查询
[DEBUG] 2018-08-09 12:47:28,652 org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
[DEBUG] 2018-08-09 12:47:28,652 org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1a69a910] was not registered for synchronization because synchronization is not active
[DEBUG] 2018-08-09 12:47:28,652 org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@755d3205] will not be managed by Spring
[DEBUG] 2018-08-09 12:47:28,652 com.jyf.ex.ehcache.dao.UserDao.updateUser - ==>  Preparing: update users set uuid = ?, name = ?, age = ? where uuid = ? 
[DEBUG] 2018-08-09 12:47:28,652 com.jyf.ex.ehcache.dao.UserDao.updateUser - ==> Parameters: 84bebbe5-b870-446f-96cc-39277e869aec(String), 张三改(String), 20(Integer), 84bebbe5-b870-446f-96cc-39277e869aec(String)
[DEBUG] 2018-08-09 12:47:28,654 com.jyf.ex.ehcache.dao.UserDao.updateUser - <==    Updates: 1
[DEBUG] 2018-08-09 12:47:28,654 org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1a69a910]
[DEBUG] 2018-08-09 12:47:28,658 com.jyf.ex.ehcache.controller.UserController - 修改用户成功
此处查询了数据库:用户UUID为84bebbe5-b870-446f-96cc-39277e869aec
[DEBUG] 2018-08-09 12:47:28,659 org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
[DEBUG] 2018-08-09 12:47:28,659 org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7b5c042d] was not registered for synchronization because synchronization is not active
[DEBUG] 2018-08-09 12:47:28,659 org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@755d3205] will not be managed by Spring
[DEBUG] 2018-08-09 12:47:28,659 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - ==>  Preparing: select * from users where uuid = ? 
[DEBUG] 2018-08-09 12:47:28,659 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - ==> Parameters: 84bebbe5-b870-446f-96cc-39277e869aec(String)
[DEBUG] 2018-08-09 12:47:28,660 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - <==      Total: 1
[DEBUG] 2018-08-09 12:47:28,660 org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7b5c042d]
User{id=6, uuid='84bebbe5-b870-446f-96cc-39277e869aec', name='张三改', age=20} //更新缓存,用数据库中的新数据替换缓存
User{id=6, uuid='84bebbe5-b870-446f-96cc-39277e869aec', name='张三改', age=20} //查询缓存  
User{id=6, uuid='84bebbe5-b870-446f-96cc-39277e869aec', name='张三改', age=20} //查询缓存
修改数据后,需要更新缓存,于是第一次查询清除缓存后从数据库查询后放入更新后的数据。
4. 删除后查询
[DEBUG] 2018-08-09 12:47:28,660 org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
[DEBUG] 2018-08-09 12:47:28,660 org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@446ec5c4] was not registered for synchronization because synchronization is not active
[DEBUG] 2018-08-09 12:47:28,660 org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@755d3205] will not be managed by Spring
[DEBUG] 2018-08-09 12:47:28,660 com.jyf.ex.ehcache.dao.UserDao.deleteUser - ==>  Preparing: delete from users where uuid = ? 
[DEBUG] 2018-08-09 12:47:28,661 com.jyf.ex.ehcache.dao.UserDao.deleteUser - ==> Parameters: 84bebbe5-b870-446f-96cc-39277e869aec(String)
[DEBUG] 2018-08-09 12:47:28,663 com.jyf.ex.ehcache.dao.UserDao.deleteUser - <==    Updates: 1
[DEBUG] 2018-08-09 12:47:28,663 org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@446ec5c4]
此处查询了数据库:用户UUID为84bebbe5-b870-446f-96cc-39277e869aec
[DEBUG] 2018-08-09 12:47:28,663 org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
[DEBUG] 2018-08-09 12:47:28,663 org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b6ce894] was not registered for synchronization because synchronization is not active
[DEBUG] 2018-08-09 12:47:28,663 org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@755d3205] will not be managed by Spring
[DEBUG] 2018-08-09 12:47:28,664 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - ==>  Preparing: select * from users where uuid = ? 
[DEBUG] 2018-08-09 12:47:28,664 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - ==> Parameters: 84bebbe5-b870-446f-96cc-39277e869aec(String)
[DEBUG] 2018-08-09 12:47:28,664 com.jyf.ex.ehcache.dao.UserDao.getUserByUuid - <==      Total: 0
[DEBUG] 2018-08-09 12:47:28,665 org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1b6ce894]
null //确认数据被删除,并更新缓存
null //查询缓存
null //查询缓存
删除数据后,应该同步更新缓存,先查询数据库确定数据已不存在,将缓存清空。因此后两次查询缓存均返回Null。