【redis事务】@Transactional对Redis事务起作用(包含redis+lua)
- 一、前言
- 二、准备
- 三、StringRedisTemplate 开启事务
- 四、关键代码(验证@Transactional对redis事务是否生效)
- 五、关键代码(验证@Transactional对redis+lua是否生效)
 
一、前言
最近工作中涉及到的事务比较多,也有用到redis事务,之前的文章也介绍过redis事务,为了方便redis使用,我们把redis事务结合springboot的@Transactional注解使用,这里为了方便大家使用,写一个demo验证一下;
注:同时也验证一下redis+lua脚本执行中,@Transactional注解事务是否生效
二、准备
为了对比事务,我们同时使用数据库(postgresql)事务和redis事务;
引入pom.xml依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!-- postgresql 配置-->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
三、StringRedisTemplate 开启事务
package com.sk.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
@Configuration
@RequiredArgsConstructor
public class RedisConfig {
    private final StringRedisTemplate stringRedisTemplate;
    @Bean
    public void getRedisTemplate(){
        stringRedisTemplate.setEnableTransactionSupport(true);
    }
}
四、关键代码(验证@Transactional对redis事务是否生效)
1、正常执行
package com.sk.service.impl;
import com.sk.mapper.StudentMapper;
import com.sk.service.AnimalService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("dogService")
@RequiredArgsConstructor
//@ConditionalOnProperty(prefix = "formatter",name="enabled",havingValue = "true")
public class DogServiceImpl implements AnimalService {
    private final StringRedisTemplate stringRedisTemplate;
    private final StudentMapper studentMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void play() {
        System.out.println("狗拿耗子!!!");
        boolean insert = studentMapper.insertUser(3,"刘英","男","123456","");
        System.out.println("pg插入结果:"+insert);
        stringRedisTemplate.opsForValue().set("3","liuying");
        System.out.println("==========执行完成======");
        //throw new RuntimeException("redis事务测试");
    }
    @Override
    public void eat() {
        System.out.println("狗爱吃骨头!!!");
    }
}
执行结果:
2022-12-11 11:46:37.268  INFO 9752 --- [nio-8089-exec-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
狗拿耗子!!!
pg插入结果:true
==========执行完成======

127.0.0.1:6379> get 3
"liuying"
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> 
2、事务回滚
package com.sk.service.impl;
import com.sk.mapper.StudentMapper;
import com.sk.service.AnimalService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("dogService")
@RequiredArgsConstructor
//@ConditionalOnProperty(prefix = "formatter",name="enabled",havingValue = "true")
public class DogServiceImpl implements AnimalService {
    private final StringRedisTemplate stringRedisTemplate;
    private final StudentMapper studentMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void play() {
        System.out.println("狗拿耗子!!!");
        boolean insert = studentMapper.insertUser(3,"刘英","男","123456","");
        System.out.println("pg插入结果:"+insert);
        stringRedisTemplate.opsForValue().set("3","liuying");
        System.out.println("==========执行完成======");
        throw new RuntimeException("redis事务测试");
    }
    @Override
    public void eat() {
        System.out.println("狗爱吃骨头!!!");
    }
}
执行结果
2022-12-11 11:51:28.699  INFO 12864 --- [nio-8089-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
狗拿耗子!!!
pg插入结果:true
==========执行完成======
2022-12-11 11:51:29.978 ERROR 12864 --- [nio-8089-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: redis事务测试] with root cause
java.lang.RuntimeException: redis事务测试
	at com.sk.service.impl.DogServiceImpl.play(DogServiceImpl.java:28) ~[classes/:na]
	at com.sk.service.impl.DogServiceImpl$$FastClassBySpringCGLIB$$6e845405.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.23.jar:5.3.23]

127.0.0.1:6379> get 5
(nil)
127.0.0.1:6379> 
127.0.0.1:6379> 
由结果可知,pg数据库和redis中数据并没有存储成功,证明事务生效;
五、关键代码(验证@Transactional对redis+lua是否生效)
1、lua脚本:
local key = KEYS[1]
redis.call("INCR",key);
2、初始化lua脚本:
/**
     * 读取限流脚本
     *
     * @return
     */
    @Bean
    public DefaultRedisScript<Number> redisluaScript() {
        DefaultRedisScript<Number> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/test.lua")));
        redisScript.setResultType(Number.class);
        return redisScript;
    }
3、正常执行
package com.sk.service.impl;
import com.sk.mapper.StudentMapper;
import com.sk.service.AnimalService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service("dogService")
@RequiredArgsConstructor
//@ConditionalOnProperty(prefix = "formatter",name="enabled",havingValue = "true")
public class DogServiceImpl implements AnimalService {
    private final StringRedisTemplate stringRedisTemplate;
    private final StudentMapper studentMapper;
    private final DefaultRedisScript defaultRedisScript;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void play() {
        System.out.println("执行lua脚本");
        List<String> keys = new ArrayList();
        keys.add("count");
        stringRedisTemplate.execute(defaultRedisScript, keys);
        System.out.println("==========执行完成======");
        //throw new RuntimeException("redis事务测试");
    }
    @Override
    public void eat() {
        System.out.println("狗爱吃骨头!!!");
    }
}
执行结果
127.0.0.1:6379> get count
"1"
127.0.0.1:6379> 
127.0.0.1:6379> 
4、执行失败
package com.sk.service.impl;
import com.sk.mapper.StudentMapper;
import com.sk.service.AnimalService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service("dogService")
@RequiredArgsConstructor
//@ConditionalOnProperty(prefix = "formatter",name="enabled",havingValue = "true")
public class DogServiceImpl implements AnimalService {
    private final StringRedisTemplate stringRedisTemplate;
    //private final StudentMapper studentMapper;
    private final DefaultRedisScript defaultRedisScript;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void play() {
        System.out.println("执行lua脚本");
        List<String> keys = new ArrayList();
        keys.add("count");
        stringRedisTemplate.execute(defaultRedisScript, keys);
        System.out.println("==========执行完成======");
        throw new RuntimeException("redis事务测试");
    }
    @Override
    public void eat() {
        System.out.println("狗爱吃骨头!!!");
    }
}
执行结果
执行lua脚本
==========执行完成======
2022-12-11 12:19:33.272 ERROR 12736 --- [nio-8089-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: redis事务测试] with root cause
java.lang.RuntimeException: redis事务测试
	at com.sk.service.impl.DogServiceImpl.play(DogServiceImpl.java:39) ~[classes/:na]
127.0.0.1:6379> get count
"1"
127.0.0.1:6379> 
127.0.0.1:6379> 
由上可知,当程序执行异常时,lua脚本中的内容同样没有生效






![[附源码]JAVA毕业设计医院管理系统(系统+LW)](https://img-blog.csdnimg.cn/13f401b6c6784d89aeb272baf962f2e0.png)








![[oeasy]python0028_直接运行_修改py文件执行权限_设置py文件打开方式](https://img-blog.csdnimg.cn/img_convert/0b387a7e4585d2722586c8b1e0376447.png)



