上篇博文学习了spring boot+mybatis+PageHelper分页插件的整合,在此基础上继续扩展,使用redis做数据库的二级缓存。对于用redis做数据库的缓存的必要性,就不多说了,特别是在少写多读的场景下(比如类似全国地区码配置表等常量配置表,或用户信息、角色、权限查询)。直接从缓存拿数据比从数据库(数据库要I/O操作)拿要快得多。
例子源码是在上篇博文的例子上修改的,就不全部贴出源码了,这只贴出和redis相关的。完整代码,在github上可下载,点击下载
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.3.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
</dependencies>
application.yml
在spring.datasource后面多加上redis相关的
spring:
datasource:
name: db
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
minIdle: 5
maxActive: 100
initialSize: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 50
removeAbandoned: true
filters: stat
cachePrepStmts: true # 开启二级缓存
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
timeout: 0
UserMapper.java
package com.fei.springboot.dao;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.fei.springboot.domain.User;
@Mapper
public interface UserMapper {
@Insert("insert sys_user(id,user_name) values(#{id},#{userName})")
void insert(User u);
@Update("update sys_user set user_name = #{userName} where id=#{id} ")
void update(User u);
@Delete("delete from sys_user where id=#{id} ")
void delete(@Param("id")String id);
@Select("select id,user_name from sys_user where id=#{id} ")
User find(@Param("id")String id);
//注:方法名和要UserMapper.xml中的id一致
List<User> query(@Param("userName")String userName);
@Delete("delete from sys_user")
void deleteAll();
}
package com.fei.springboot.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.fei.springboot.dao.UserMapper;
import com.fei.springboot.domain.User;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
@Service
@CacheConfig(cacheNames="userCache") // 本类内方法指定使用缓存时,默认的名称就是userCache
@Transactional(propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class)
public class UserService {
@Autowired
private UserMapper userMapper;
// 因为必须要有返回值,才能保存到数据库中,如果保存的对象的某些字段是需要数据库生成的,
//那保存对象进数据库的时候,就没必要放到缓存了
@CachePut(key="#p0.id") //#p0表示第一个参数
//必须要有返回值,否则没数据放到缓存中
public User insertUser(User u){
this.userMapper.insert(u);
//u对象中可能只有只几个有效字段,其他字段值靠数据库生成,比如id
return this.userMapper.find(u.getId());
}
@CachePut(key="#p0.id")
public User updateUser(User u){
this.userMapper.update(u);
//可能只是更新某几个字段而已,所以查次数据库把数据全部拿出来全部
return this.userMapper.find(u.getId());
}
@Cacheable(key="#p0") // @Cacheable 会先查询缓存,如果缓存中存在,则不执行方法
public User findById(String id){
System.err.println("根据id=" + id +"获取用户对象,从数据库中获取");
return this.userMapper.find(id);
}
@CacheEvict(key="#p0") //删除缓存名称为userCache,key等于指定的id对应的缓存
public void deleteById(String id){
this.userMapper.delete(id);
}
//清空缓存名称为userCache(看类名上的注解)下的所有缓存
//如果数据失败了,缓存时不会清除的
@CacheEvict(allEntries = true)
public void deleteAll(){
this.userMapper.deleteAll();
}
public PageInfo<User> queryPage(String userName,int pageNum,int pageSize){
Page<User> page = PageHelper.startPage(pageNum, pageSize);
//PageHelper会自动拦截到下面这查询sql
this.userMapper.query(userName);
return page.toPageInfo();
}
}
用到了注解@CacheConfig,@CachePut,@Cacheable
@CachePut 是先执行方法,然后把返回值保存或更新到缓存中
@Cacheable 是先查询缓存,如果缓存有值,则不执行方法了;否则执行方法,然后把返回值保存到缓存
更多的信息可自行百度
UserController.java
package com.fei.springboot.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fei.springboot.domain.User;
import com.fei.springboot.service.UserService;
import com.github.pagehelper.PageInfo;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello";
}
/**
* 测试插入
* @return
*/
@RequestMapping("/add")
@ResponseBody
public String add(String id,String userName){
User u = new User();
u.setId(id);
u.setUserName(userName);
this.userService.insertUser(u);
return u.getId()+" " + u.getUserName();
}
/**
* 测试根据id查询
* @return
*/
@RequestMapping("/get/{id}")
@ResponseBody
public String findById(@PathVariable("id")String id){
User u = this.userService.findById(id);
return u== null ? "找不到对象" :( u.getId()+" " + u.getUserName());
}
/**
* 测试修改
* @return
*/
@RequestMapping("/update")
@ResponseBody
public String update(String id,String userName){
User u = new User();
u.setId(id);
u.setUserName(userName);
this.userService.updateUser(u);
return u.getId()+" " + u.getUserName();
}
/**
* 测试删除
* @return
*/
@RequestMapping("/delete/{id}")
@ResponseBody
public String delete(@PathVariable("id")String id){
this.userService.deleteById(id);
return "success";
}
/**
* 测试全部
* @return
*/
@RequestMapping("/deleteAll")
@ResponseBody
public String deleteAll(){
this.userService.deleteAll();
return "success";
}
/**
* 测试分页插件
* @return
*/
@RequestMapping("/queryPage")
@ResponseBody
public String queryPage(){
PageInfo<User> page = this.userService.queryPage("tes", 1, 2);
System.out.println("总页数=" + page.getPages());
System.out.println("总记录数=" + page.getTotal()) ;
for(User u : page.getList()){
System.out.println(u.getId() + " \t " + u.getUserName());
}
return page.toString();
}
}
使用redisClient对redis进行查看
测试:
1.新增用户123 test123时,redis中查看到数据
2.更新用户123,redis和数据库都看到变更后的信息
3.查询用户123 时,控制台没看到查询方法中写的打印信息,说明走的是缓存
4.删除用户123,redisClient、和数据库都没看到记录了
5.查询用户123,控制台有看到查询方法中写的打印信息,说明缓存中没有,执行了方法,企图从数据库中拿数据
6.新增用户123,456,redis和数据库中都看到数据
7.删除全部,redis和数据库都清空了
经过上面的测试,发现redis达到了想要的效果。