本文整合的redis客户端是lettucce,话不多说上代码。
先配置一个RedisTemplete的实例,在里面指定redis支持数据库的事务
@Bean public RedisTemplate redisTemplate(LettuceConnectionFactory factory){ RedisTemplate redisTemplate = new RedisTemplate(); //使用LettuceConnectionFactory 代替 RedisConnectionFactory redisTemplate.setConnectionFactory(factory); //配置序列化方式 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); //设置redis支持数据库的事务 redisTemplate.setEnableTransactionSupport(true); return redisTemplate; }
写个简单demo测试下事务是否有效
@Transactional(rollbackFor = Exception.class) public void changeCount2(String name) throws Exception{ redisTemplate.opsForValue().set(name,123); dotest(); redisTemplate.opsForValue().set(name,345); }
public void dotest() throws Exception{ rabbitBackUpDao.insert(RabbitBackUp.builder() .exchange("asdf") .routingKey("fdsa") .correlationDataId("1234123ads") .data("qwerty") .build()); throw new Exception(); }
亲测redis存进去的值随着mysql的事务回滚掉了,你不是说有问题吗?问题在哪呢?下面我就列出几种情况
写个乐观锁的demo
@Transactional(rollbackFor = Exception.class) public void changeCount2(String name) throws Exception{ redisTemplate.watch(name); Integer number = (Integer)redisTemplate.opsForValue().get(name); number ++; redisTemplate.multi(); redisTemplate.opsForValue().set(name,number); redisTemplate.exec(); dotest(); redisTemplate.opsForValue().set(name,345); }
报错
看下源码
它说事务已经开启了,再watch是不被允许的。
你不让我乐观锁了,那我就去掉,看看还会有啥问题
@Transactional(rollbackFor = Exception.class) public void changeCount2(String name) throws Exception{ //redisTemplate.watch(name); Integer number = (Integer)redisTemplate.opsForValue().get(name); number ++; redisTemplate.multi(); redisTemplate.opsForValue().set(name,number); redisTemplate.exec(); dotest(); redisTemplate.opsForValue().set(name,345); }
结果是number是个空,为什么会是空呢?再看代码
结果加入到了一个队列中,然后返了null
为什么会出现这两种情况?
其实redis的事务本身就是一组命令集合,multi用来标记事务开始,exec用来执行之间的命令,由于贴有@Transactional标签加上templete的事务设置,框架会自动去帮我们开启和执行事务,所以,一开始isMulti就是ture,当我们get的时候,此时并不会立即执行这条命令,而是等框架给我们exec的时候去真正执行返回结果,那么立即返回了个null,所以使用了@Transactional和redisTemplate.setEnableTransactionSupport(true)在本方法中不能使用get去取值或者去watch,也不要去mutli和exec了,正常执行set就行了。