在SpringBoot项目中使用Redis进行缓存接口返回数据,以及结合课程表的增删查改进行获取更新缓存。
一、相关注解 @Cacheable、@CachePut、@CacheEvict
在SpringBoot项目中我们进行缓存接口数据主要使用的是Spring的缓存注解 @Cacheable、@CachePut、@CacheEvict
@Cacheable:表示当访问到改方法上首先从缓存中获取,如果缓存中没有则会执行方法内部代码,并将代码返回的数据存在缓存中;
@CachePut:表示每次请求调用该方法后都会执行函数内部代码,将返回数据存放在注解所指定的key中;
@CacheEvict:表示执行函数内部代码后,并将注解所指定的key进行删除;
二、在我们的场景中使用缓存
我们现在需要写一个课程列表查询(根据教师编号),以及课程添加、修改、删除。我们将缓存添加在业务实现类上面
com.demo.serviceImpl.CourseServiceImpl
1,给课程列表查询添加缓存,key为教师编号
/**
* 给某个教师的课程列表添加缓存,如果有缓存直接返回缓存结果,没有查询添加缓存
*/
@Cacheable(value="redisCache",key="#course.userId")
@Override
public List<Course> getCourList(Course course) {
CourseExample example = new CourseExample();
CourseExample.Criteria createCriteria = example.createCriteria();
String userId = course.getUserId();
if(userId!= null && userId!= ""){
createCriteria.andUserIdEqualTo(userId);
}
List<Course> cours = courseDao.selectByExample(example);
return cours;
}
@Cacheable中的value表示缓存存储在哪一个redis库中,key使用了SpringEL的表达式#开头。
2,添加课程后,以课程id为缓存key添加缓存,并更新改课程老师的列表缓存 cacheService.updateCourListCache。
/**
* 新增课程,并清除该教师下的课程列表缓存
*/
@CachePut(value="redisCache",key="#course.courId")
@Override
public int addCourse(Course course) {
int insertSelective = courseDao.insertSelective(course);
if(insertSelective > 0){
cacheService.updateCourListCache(course);
}
return insertSelective;
}
3,删除课程,并删除课程缓存,更新教师课程列表缓存
/**
* 删除课程,删除课程缓存,更新教师课程列表缓存
*/
@CacheEvict(value="redisCache",key="#course.courId")
@Override
public int delteCour(Course course) {
String courId = course.getCourId();
int deleteNum = courseDao.deleteByPrimaryKey(courId );
if(deleteNum>0){
cacheService.updateCourListCache(course);
}
return deleteNum;
}
注意:在更新教师课程列表缓存的时候我们没有直接调用该类内部的getCourList方法,因为在同一个类中调用其他缓存方法,被调用方法缓存注解实际是无效的。这时我们需要新写一个接口,在此类中注入该接口实现类,在另一个接口实现类内部去更新缓存。
@Service
public class CacheServiceImpl implements CacheService{
@Autowired
private CourseDao courseDao;
@CachePut(value="redisCache",key="#course.userId")
@Override
public List<Course> updateCourListCache(Course course) {
CourseExample example = new CourseExample();
List<Course> cours = courseDao.selectByExample(example);
return cours;
}
}
同理我们的修改课程,删除课程都会通过注入缓存实现类去更新列表缓存。
三、我们的课程列表前台页面以及主要后台类以及配置文件如下:
├─main
│ ├─java
│ │ └─com
│ │ └─demo
│ │ │ Application.java
│ │ ├─bean
│ │ │ Course.java
│ │ │ CourseExample.java
│ │ │ User.java
│ │ ├─controller
│ │ │ CourController.java
│ │ │ CourseController.java
│ │ │ PageController.java
│ │ │ UserController.java
│ │ ├─dao
│ │ │ CourseDao.java
│ │ │ CourseMapper.xml
│ │ ├─filter
│ │ │ LoginFilter.java
│ │ ├─redis
│ │ │ RedisConfig.java
│ │ │ RedisServiceImpl.java
│ │ ├─service
│ │ │ CacheService.java
│ │ │ CourseService.java
│ │ └─serviceImpl
│ │ CacheServiceImpl.java
│ │ CourseServiceImpl.java
│ └─resources
│ │ application.properties
│ │ mybatis-config.xml
│ ├─static
│ │ index.html
│ └─templates
│ add.html
│ courList.html
│ header.html
│ index.html
└─test
├─java
└─resources
/bootdemo/src/main/resources/templates/courList.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
<style type="text/css">
.odd{background: #ccc;}
table{ border-collapse:collapse; }
</style>
</head>
<body>
<a th:href="@{/course/courInput}">新增</a>
<table border="1" >
<tr><th>id</th><th>name</th><th>cour_desc</th><th>update</th></tr>
<tr th:each="cour,courStat : ${courList}" th:class="${courStat.odd}? 'odd'" th:index="${courStat.index}" th:count ="${courStat.count}" th:isFirst="${courStat.first}" th:isLast="${courStat.Last}">
<td th:text="${cour.courId}">Onions</td>
<td th:text="${cour.courName}">Onions</td>
<td th:text="${cour.courDesc}">Onions</td>
<td >
<a th:href="@{/course/courInput(courId=${cour.courId})}">修改</a>
<a th:href="@{/course/deleteCour(courId=${cour.courId})}">删除</a>
</td>
</tr>
</table>
</body>
</html>
/bootdemo/src/main/resources/templates/add.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
<style type="text/css">
</style>
</head>
<body>
<form method="post" th:action="@{/course/{method}/(method=${course.courId !=null ? 'updateCour':'addCour'})}">
<input name="courId" type="hidden" th:value="${course.courId}" />
<input name="userId" type="hidden" th:value="${course.userId}" />
<table>
<tr><td>name</td><td><input name="courName" th:value="${course.courName}" /> </td></tr>
<tr><td>desc</td><td><input name="courDesc" th:value="${course.courDesc}"/> </td></tr>
<tr><td colspan="2"><button type="submit">提交</button></td></tr>
</table>
</form>
</body>
</html>
com.demo.controller.CourseController
@Controller
@RequestMapping(value="/course")
public class CourseController {
@Autowired
private RedisServiceImpl redisService;
@Autowired
private CourseService courseService;
/**
* 获取课程列表
* @param course
* @param mp
* @return
*/
@RequestMapping(value="/getCourList",method=RequestMethod.GET)
public String getCourList(Course course,ModelMap mp){
List<Course> list = courseService.getCourList(course);
mp.put("courList", list);
return "courList";
}
/**
* 跳转表单页面,根据课程编号是否为空判断
* @param course
* @param mp
* @return
*/
@RequestMapping(value="/courInput",method=RequestMethod.GET)
public String courAdd(Course course,ModelMap mp){
if(course != null && course.getCourId() != null && course.getCourId() != ""){
Course course2 = courseService.getCourse(course);
mp.put("course", course2);
}
System.out.println(course);
return "add";
}
/**
* 新增课程,放课程详情缓存,更新列表缓存
* @param course
* @param mp
* @return
*/
@RequestMapping(value="/addCour",method=RequestMethod.POST)
public String Addcour(Course course,ModelMap mp){
String userId = "t0001";//应该session获取
course.setCourId(System.currentTimeMillis()+"");
course.setUserId(userId);
courseService.addCourse(course);
return "redirect:/course/getCourList?userId="+userId;
}
/**
* 放课程详情缓存,更新列表缓存
* @param course
* @param mp
* @return
*/
@RequestMapping(value="/updateCour",method=RequestMethod.POST)
public String updateCour(Course course,ModelMap mp){
course.setUserId("t0001");
courseService.updateCourse(course);
return "redirect:/course/getCourList?userId=t0001";
}
/**
* 课程删除 删除详情缓存,更新列表缓存
* @param course
* @param mp
* @return
*/
@RequestMapping(value="/deleteCour",method=RequestMethod.GET)
public String deleteCour(Course course,ModelMap mp){
course.setUserId("t0001");
courseService.delteCour(course);
return "redirect:/course/getCourList?userId=t0001";
}
//==================== redisTemplate 测试 =========================
@RequestMapping(value="/setCourVal",method=RequestMethod.GET)
public void setCourVal(String val){
redisService.set("testkey", val);
}
@ResponseBody
@RequestMapping(value="/getCourVal",method=RequestMethod.GET)
public String getCourVal(String val){
return redisService.get("testkey");
}
}
/bootdemo/src/main/java/com/demo/serviceImpl/CourseServiceImpl.java
@Service
public class CourseServiceImpl implements CourseService {
@Autowired
private CacheService cacheService;
@Autowired
private CourseDao courseDao;
/**
* 给某个教师的课程列表添加缓存,如果有缓存直接返回缓存结果,没有查询添加缓存
*/
@Cacheable(value="redisCache",key="#course.userId")
@Override
public List<Course> getCourList(Course course) {
CourseExample example = new CourseExample();
CourseExample.Criteria createCriteria = example.createCriteria();
String userId = course.getUserId();
if(userId!= null && userId!= ""){
createCriteria.andUserIdEqualTo(userId);
}
List<Course> cours = courseDao.selectByExample(example);
return cours;
}
/**
* 新增课程,并清除该教师下的课程列表缓存
*/
@CachePut(value="redisCache",key="#course.courId")
@Override
public Course addCourse(Course course) {
int insertSelective = courseDao.insertSelective(course);
if(insertSelective > 0){
cacheService.updateCourListCache(course);
return course;
}
return null;
}
/**
* 删除课程,删除课程缓存,更新教师课程列表缓存
*/
@CacheEvict(value="redisCache",key="#course.courId")
@Override
public int delteCour(Course course) {
String courId = course.getCourId();
int deleteNum = courseDao.deleteByPrimaryKey(courId );
if(deleteNum>0){
cacheService.updateCourListCache(course);
}
return deleteNum;
}
@CacheEvict(value="redisCache",key="#course.courId")
@Override
public int updateCourse(Course course) {
int updateNum = courseDao.updateByPrimaryKeySelective(course);
if(updateNum > 0){
cacheService.updateCourListCache(course);
}
return updateNum;
}
@Cacheable(value="redisCache",key="#course.courId")
@Override
public Course getCourse(Course course) {
return courseDao.selectByPrimaryKey(course.getCourId());
}
}
com.demo.serviceImpl.CacheServiceImpl
@Service
public class CacheServiceImpl implements CacheService{
@Autowired
private CourseDao courseDao;
@CachePut(value="redisCache",key="#course.userId")
@Override
public List<Course> updateCourListCache(Course course) {
CourseExample example = new CourseExample();
List<Course> cours = courseDao.selectByExample(example);
return cours;
}
}
四、测试验证
测试新增,控制台打印如下:
LocalAddr:/WebDemo/course/getCourList
LocalAddr:/WebDemo/course/courInput
com.demo.bean.Course@33e6d4a3
LocalAddr:/WebDemo/course/addCour/
2019-05-25 16:02:09.108 DEBUG 6624 --- [nio-8080-exec-7] com.demo.dao.CourseDao.insertSelective : ==> Preparing: insert into course ( cour_id, user_id, cour_name, cour_desc ) values ( ?, ?, ?, ? )
2019-05-25 16:02:09.114 DEBUG 6624 --- [nio-8080-exec-7] com.demo.dao.CourseDao.insertSelective : ==> Parameters: 1558771329097(String), t0001(String), Spring Boot 缓存应用(String), Reids缓存使用的概述(String)
2019-05-25 16:02:09.156 DEBUG 6624 --- [nio-8080-exec-7] com.demo.dao.CourseDao.insertSelective : <== Updates: 1
2019-05-25 16:02:09.172 DEBUG 6624 --- [nio-8080-exec-7] com.demo.dao.CourseDao.selectByExample : ==> Preparing: select cour_id, user_id, cour_name, cour_desc, cour_file, cour_file_name, sta_time, end_time, full_score, state, cour_dir_id, cour_create_time from course
2019-05-25 16:02:09.173 DEBUG 6624 --- [nio-8080-exec-7] com.demo.dao.CourseDao.selectByExample : ==> Parameters:
2019-05-25 16:02:09.182 DEBUG 6624 --- [nio-8080-exec-7] com.demo.dao.CourseDao.selectByExample : <== Total: 14
LocalAddr:/WebDemo/course/getCourList
如上我们可以看到当我们在课程列表页面点击新增后进入新增页面,在输入完成后点击提交在新增课程接口中首先进行了一次数据库新增操作,操作完成后调用列表缓存类去数据库查询使用@CachePut(value="redisCache",key="#course.userId")注解重新查询重置了下列表缓存,所以当我们新增接口调用结束,返回到列表视图的时候,缓存已经更新,此时类别中已经有了我们新增的该课程。
我们在点击修改课程看看控制台打印:
LocalAddr:/WebDemo/course/courInput
com.demo.bean.Course@3efb9a80
提交
LocalAddr:/WebDemo/course/updateCour/
2019-05-25 16:34:21.805 DEBUG 3364 --- [nio-8080-exec-6] c.d.d.C.updateByPrimaryKeySelective : ==> Preparing: update course SET user_id = ?, cour_name = ?, cour_desc = ? where cour_id = ?
2019-05-25 16:34:21.893 DEBUG 3364 --- [nio-8080-exec-6] c.d.d.C.updateByPrimaryKeySelective : ==> Parameters: t0001(String), Spring Boot 缓存应用2(String), Reids缓存使用的概述2(String), 1558771329097(String)
2019-05-25 16:34:21.964 DEBUG 3364 --- [nio-8080-exec-6] c.d.d.C.updateByPrimaryKeySelective : <== Updates: 1
2019-05-25 16:34:22.013 DEBUG 3364 --- [nio-8080-exec-6] com.demo.dao.CourseDao.selectByExample : ==> Preparing: select cour_id, user_id, cour_name, cour_desc, cour_file, cour_file_name, sta_time, end_time, full_score, state, cour_dir_id, cour_create_time from course
2019-05-25 16:34:22.014 DEBUG 3364 --- [nio-8080-exec-6] com.demo.dao.CourseDao.selectByExample : ==> Parameters:
2019-05-25 16:34:22.091 DEBUG 3364 --- [nio-8080-exec-6] com.demo.dao.CourseDao.selectByExample : <== Total: 14
LocalAddr:/WebDemo/course/getCourList
同样他回现在数据库做一个更新操作,并将列表缓存也进行刷新一次。
SpringBoot缓存配置上部分,请查看此博文 https://blog.csdn.net/liuhenghui5201/article/details/90544737
项目代码变更记录:
https://github.com/liuhenghui/SpringBootDemo/commit/a6504131d337e25d89fe2765cd69f4672d47351c
https://github.com/liuhenghui/SpringBootDemo/commit/fecd3d1eee47cb61b00881038d15ca9b91eb298f