Spring boot redis的使用
2017-08-22 14:31
337 查看
源码地址https://github.com/yejingtao/forblog.git中mybank-jpa-redis项目。
上篇博文讲解了spring boot web+data的用法,本篇在前面基础上扩展下缓存和事务的功能。
内存的使用能提高io读写速度,但是单机的JVM存在内存较小、跨结点无法共享的缺陷,所以需要掌握一种缓存技术来解决单机缺陷,我们这里选择的是Redis。
本地开发和测试推荐docker容器,docker刚上手比较难,概念上的东西不好理解,但是真正使用起来方便的飞起,因为我们不用再重复制造轮子,可以直接搬来用。
本篇重点介绍Redis在Spring
data中的用法和事务如何控制,后续会专门做redis和docker的专题,欢迎持续关注。
POM依赖添加对redis的使用:
配置文件增加redis地址的配置:
主程序Application添加注解@EnableCaching,标识开启缓存。
Entity层实现Serializable,要将复杂对象序列化方式缓存到redis中。
Service层增加缓存的注解。
@Cacheable(查询缓存,每次查询以一个标识作为key,如果再次执行本查询方法根据key判断是直接返回缓存结果还是去数据库获取)
@CacheEvict(清理缓存,一般用在delete方法中)
@CachePut(重新设置缓存,一般用户update或save方法中)
如果一个方法需要对多组缓存做处理需要使用@Caching注解,入参是数组类型的@CacheEvict或者@CachePut注解。
findUserByName方法根据入参name为key保存到缓存中,redis缓存的name是userInfo
findOperationByUserId方法根据入参userId为key保存到缓存中,redis缓存中的name是operationInfo
createOrUpdate方法会新增或者修改user和operation记录,如果不处理缓存那么之后的find方法得到的数据与数据库不同步了,所以这里我们清理掉
userInfo和operationInfo缓存,让他们下次执行重新去数据库加载。
以上是redis作为二级缓存的用法,用来减少数据的压力提高响应速率,redis还有个用法是作为架构的系统缓存,例如加载初始化属性、静态资源、结点间共享数据等,MainController中有此种用法。
我们想维护用户最后一次更改操作的时间点,可以通过redisClient+log中打印的sql来判断缓存是否生效。
注意:做系统缓存时我层尝试将date作为最后个参数传入HashOperations会报错java.lang.ClassCastException:
java.util.Date cannot be cast tojava.lang.String,这里只能传入字符串。
解决了缓存问题我们最后来看一下Spring data事务是如何解决的。
最简洁的办法是在需要事务控制的方法上添加一个无任何参数的@Transactional就搞得定,本质上等价于@Transactional(isolation
= Isolation.DEFAULT,propagation =Propagation.REQUIRED)
事务有2个参数隔离级别和传播行为。
隔离级别Isolation:事物之间的隔离程度,脏读、重复读、幻读。
看下源码就能了解有哪些级别都是什么意义
传播行为Propagation:上午上下文直接对事物的传递是如何处理的。
依旧需要看源码才能更好理解
上篇博文讲解了spring boot web+data的用法,本篇在前面基础上扩展下缓存和事务的功能。
内存的使用能提高io读写速度,但是单机的JVM存在内存较小、跨结点无法共享的缺陷,所以需要掌握一种缓存技术来解决单机缺陷,我们这里选择的是Redis。
本地开发和测试推荐docker容器,docker刚上手比较难,概念上的东西不好理解,但是真正使用起来方便的飞起,因为我们不用再重复制造轮子,可以直接搬来用。
本篇重点介绍Redis在Spring
data中的用法和事务如何控制,后续会专门做redis和docker的专题,欢迎持续关注。
POM依赖添加对redis的使用:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
配置文件增加redis地址的配置:
server: port: 8081 spring: datasource: url: jdbc:mysql://192.168.226.130:3306/schema1 username: root password: admin123 driver-class-name: com.mysql.jdbc.Driver redis: database: 0 port: 6379 host: 192.168.226.130 pool: max-idle: 8 min-idle: 0 max-active: 8 max-wait: -1 jpa: database: MYSQL show-sql: true application: name: mybank
主程序Application添加注解@EnableCaching,标识开启缓存。
Entity层实现Serializable,要将复杂对象序列化方式缓存到redis中。
Service层增加缓存的注解。
@Cacheable(查询缓存,每次查询以一个标识作为key,如果再次执行本查询方法根据key判断是直接返回缓存结果还是去数据库获取)
@CacheEvict(清理缓存,一般用在delete方法中)
@CachePut(重新设置缓存,一般用户update或save方法中)
如果一个方法需要对多组缓存做处理需要使用@Caching注解,入参是数组类型的@CacheEvict或者@CachePut注解。
@Override @Cacheable(value="userInfo", key="#name") public List<User> findUserByName(String name) { return userRepository.findByName(name); } @Override @Cacheable(value="operationInfo", key="#userId") public List<Operation> findOperationByUserId(int userId) { return operationRepository.findByUserId(userId); } @Override @Caching(evict = { @CacheEvict(value="userInfo", key="#user.name"), @CacheEvict(value="operationInfo", key="#user.id")}) @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED) //@Transactional public User createOrUpdate(User user,String description) { user = userRepository.save(user); Operation operation = new Operation(user.getId(),description); operationRepository.save(operation); return user; }
findUserByName方法根据入参name为key保存到缓存中,redis缓存的name是userInfo
findOperationByUserId方法根据入参userId为key保存到缓存中,redis缓存中的name是operationInfo
createOrUpdate方法会新增或者修改user和operation记录,如果不处理缓存那么之后的find方法得到的数据与数据库不同步了,所以这里我们清理掉
userInfo和operationInfo缓存,让他们下次执行重新去数据库加载。
以上是redis作为二级缓存的用法,用来减少数据的压力提高响应速率,redis还有个用法是作为架构的系统缓存,例如加载初始化属性、静态资源、结点间共享数据等,MainController中有此种用法。
/** * 创建用户 * @param user * @return */ @RequestMapping(value="/saveUser",method=RequestMethod.POST) public String create(ModelMap modelMap,User user) { String description = user.getId()>0?("update money into "+user.getCurrent()):"register a new user"; User thisUser = userService.createOrUpdate(user,description); modelMap.addAttribute("thisUser", thisUser); modelMap.addAttribute("thisOperations", userService.findOperationByUserId(thisUser.getId())); //这里是系统缓存的用法 HashOperations<String, String, String> hashOp = redisTemplate.opsForHash(); hashOp.put(REDIS_KEY, thisUser.getName(), (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()));//user latest modify time return "myuser"; }
@RequestMapping(value="/find",method=RequestMethod.GET) @ResponseBody public String findUserInfo(@NotNull String name) { StringBuffer returnSb = new StringBuffer(); List<User> userList = userService.findUserByName(name); returnSb.append("User: " + userList.get(0).toString() + "\\r\\n"); //这里是系统缓存的用法 HashOperations<String, String, String> hashOp = redisTemplate.opsForHash(); String userLatestModifyTime = hashOp.get(REDIS_KEY, userList.get(0).getName());//user latest modify time returnSb.append("User latest modify time: " + userLatestModifyTime + "\\r\\n"); returnSb.append("Operation: "); List<Operation> operationList = userService.findOperationByUserId(userList.get(0).getId()); if(operationList!=null && operationList.size()>0) { operationList.forEach(o->returnSb.append(o)); } return returnSb.toString(); }
我们想维护用户最后一次更改操作的时间点,可以通过redisClient+log中打印的sql来判断缓存是否生效。
注意:做系统缓存时我层尝试将date作为最后个参数传入HashOperations会报错java.lang.ClassCastException:
java.util.Date cannot be cast tojava.lang.String,这里只能传入字符串。
解决了缓存问题我们最后来看一下Spring data事务是如何解决的。
最简洁的办法是在需要事务控制的方法上添加一个无任何参数的@Transactional就搞得定,本质上等价于@Transactional(isolation
= Isolation.DEFAULT,propagation =Propagation.REQUIRED)
事务有2个参数隔离级别和传播行为。
隔离级别Isolation:事物之间的隔离程度,脏读、重复读、幻读。
看下源码就能了解有哪些级别都是什么意义
public enum Isolation { DEFAULT(TransactionDefinition.ISOLATION_DEFAULT), READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED), READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED), REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ), SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); private final int value; Isolation(int value) { this.value = value; } public int value() { return this.value; } }
传播行为Propagation:上午上下文直接对事物的传递是如何处理的。
依旧需要看源码才能更好理解
public enum Propagation { REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED), SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS), MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY), REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW), NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED), NEVER(TransactionDefinition.PROPAGATION_NEVER), NESTED(TransactionDefinition.PROPAGATION_NESTED); private final int value; Propagation(int value) { this.value = value; } public int value() { return this.value; } }
相关文章推荐
- Spring Boot中Redis的使用
- Spring Boot 基于注解的 Redis 缓存使用
- Spring Boot中的缓存支持(二)使用Redis做集中式缓存
- 使用Java(Springboot)操作Redis
- SpringBoot系列-Redis使用
- Spring Boot使用Redis进行消息的发布订阅
- Spring Boot教程(二)使用Redis做集中式缓存
- SpringBoot使用Redis做缓存,@Cacheable、@CachePut、@CacheEvict等注解的使用
- Spring Boot中Redis数据库的使用实例
- spring boot session redis的使用
- Spring Boot 系列 | 第五篇:使用Redis
- springboot整合redis使用nginx实现负载均衡实现session共享
- Redis系列三 - Spring boot如何使用redis做缓存及缓存注解的用法总结
- Spring Boot(4)—— Spring Boot中Redis的使用
- spring boot-使用redis的Keyspace Notifications实现定时任务队列
- spring boot中使用resid-RedisTemplate
- Spring Boot系列(七)Spring Boot使用Redis实现session共享
- Spring Boot中使用Redis数据库
- REDIS学习(3.2)spring boot 使用redis作为缓存
- 使用Java(Springboot)操作Redis