Spring boot redis的使用

    源码地址https://github.com/yejingtao/forblog.gitmybank-jpa-redis项目。

    上篇博文讲解了spring boot web+data的用法,本篇在前面基础上扩展下缓存和事务的功能。

内存的使用能提高io读写速度,但是单机的JVM存在内存较小、跨结点无法共享的缺陷,所以需要掌握一种缓存技术来解决单机缺陷,我们这里选择的是Redis

    本地开发和测试推荐docker容器,docker刚上手比较难,概念上的东西不好理解,但是真正使用起来方便的飞起,因为我们不用再重复制造轮子,可以直接搬来用。

    本篇重点介绍RedisSpring data中的用法和事务如何控制,后续会专门做redisdocker的专题,欢迎持续关注。

    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(重新设置缓存,一般用户updatesave方法中)

如果一个方法需要对多组缓存做处理需要使用@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方法根据入参namekey保存到缓存中,redis缓存的nameuserInfo

        findOperationByUserId方法根据入参userIdkey保存到缓存中,redis缓存中的nameoperationInfo

        createOrUpdate方法会新增或者修改useroperation记录,如果不处理缓存那么之后的find方法得到的数据与数据库不同步了,所以这里我们清理掉     userInfooperationInfo缓存,让他们下次执行重新去数据库加载。

    以上是 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; }

}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值