热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【redis事务】@Transactional对Redis事务起作用(包含redis+lua)

【redis事务】Transactional对Redis事务起作用(包含redislua)一、前言二、准备三、StringRedisTemplate开启




【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;
&#64;Configuration
&#64;RequiredArgsConstructor
public class RedisConfig {
private final StringRedisTemplate stringRedisTemplate;
&#64;Bean
public void getRedisTemplate(){
stringRedisTemplate.setEnableTransactionSupport(true);
}
}

四、关键代码&#xff08;验证&#64;Transactional对redis事务是否生效&#xff09;

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;
&#64;Service("dogService")
&#64;RequiredArgsConstructor
//&#64;ConditionalOnProperty(prefix &#61; "formatter",name&#61;"enabled",havingValue &#61; "true")
public class DogServiceImpl implements AnimalService {
private final StringRedisTemplate stringRedisTemplate;
private final StudentMapper studentMapper;
&#64;Override
&#64;Transactional(rollbackFor &#61; Exception.class)
public void play() {
System.out.println("狗拿耗子&#xff01;&#xff01;&#xff01;");
boolean insert &#61; studentMapper.insertUser(3,"刘英","男","123456","");
System.out.println("pg插入结果:"&#43;insert);
stringRedisTemplate.opsForValue().set("3","liuying");
System.out.println("&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;执行完成&#61;&#61;&#61;&#61;&#61;&#61;");
//throw new RuntimeException("redis事务测试");
}
&#64;Override
public void eat() {
System.out.println("狗爱吃骨头&#xff01;&#xff01;&#xff01;");
}
}

执行结果&#xff1a;

2022-12-11 11:46:37.268 INFO 9752 --- [nio-8089-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
狗拿耗子&#xff01;&#xff01;&#xff01;
pg插入结果:true
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;执行完成&#61;&#61;&#61;&#61;&#61;&#61;

在这里插入图片描述

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;
&#64;Service("dogService")
&#64;RequiredArgsConstructor
//&#64;ConditionalOnProperty(prefix &#61; "formatter",name&#61;"enabled",havingValue &#61; "true")
public class DogServiceImpl implements AnimalService {
private final StringRedisTemplate stringRedisTemplate;
private final StudentMapper studentMapper;
&#64;Override
&#64;Transactional(rollbackFor &#61; Exception.class)
public void play() {
System.out.println("狗拿耗子&#xff01;&#xff01;&#xff01;");
boolean insert &#61; studentMapper.insertUser(3,"刘英","男","123456","");
System.out.println("pg插入结果:"&#43;insert);
stringRedisTemplate.opsForValue().set("3","liuying");
System.out.println("&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;执行完成&#61;&#61;&#61;&#61;&#61;&#61;");
throw new RuntimeException("redis事务测试");
}
&#64;Override
public void eat() {
System.out.println("狗爱吃骨头&#xff01;&#xff01;&#xff01;");
}
}

执行结果

2022-12-11 11:51:28.699 INFO 12864 --- [nio-8089-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
狗拿耗子&#xff01;&#xff01;&#xff01;
pg插入结果:true
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;执行完成&#61;&#61;&#61;&#61;&#61;&#61;
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>

由结果可知&#xff0c;pg数据库redis中数据并没有存储成功&#xff0c;证明事务生效&#xff1b;


五、关键代码&#xff08;验证&#64;Transactional对redis&#43;lua是否生效&#xff09;

1、lua脚本&#xff1a;

local key &#61; KEYS[1]
redis.call("INCR",key);

2、初始化lua脚本&#xff1a;

/**
* 读取限流脚本
*
* &#64;return
*/

&#64;Bean
public DefaultRedisScript<Number> redisluaScript() {
DefaultRedisScript<Number> redisScript &#61; 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;
&#64;Service("dogService")
&#64;RequiredArgsConstructor
//&#64;ConditionalOnProperty(prefix &#61; "formatter",name&#61;"enabled",havingValue &#61; "true")
public class DogServiceImpl implements AnimalService {
private final StringRedisTemplate stringRedisTemplate;
private final StudentMapper studentMapper;
private final DefaultRedisScript defaultRedisScript;
&#64;Override
&#64;Transactional(rollbackFor &#61; Exception.class)
public void play() {
System.out.println("执行lua脚本");
List<String> keys &#61; new ArrayList();
keys.add("count");
stringRedisTemplate.execute(defaultRedisScript, keys);
System.out.println("&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;执行完成&#61;&#61;&#61;&#61;&#61;&#61;");
//throw new RuntimeException("redis事务测试");
}
&#64;Override
public void eat() {
System.out.println("狗爱吃骨头&#xff01;&#xff01;&#xff01;");
}
}

执行结果

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;
&#64;Service("dogService")
&#64;RequiredArgsConstructor
//&#64;ConditionalOnProperty(prefix &#61; "formatter",name&#61;"enabled",havingValue &#61; "true")
public class DogServiceImpl implements AnimalService {
private final StringRedisTemplate stringRedisTemplate;
//private final StudentMapper studentMapper;
private final DefaultRedisScript defaultRedisScript;
&#64;Override
&#64;Transactional(rollbackFor &#61; Exception.class)
public void play() {
System.out.println("执行lua脚本");
List<String> keys &#61; new ArrayList();
keys.add("count");
stringRedisTemplate.execute(defaultRedisScript, keys);
System.out.println("&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;执行完成&#61;&#61;&#61;&#61;&#61;&#61;");
throw new RuntimeException("redis事务测试");
}
&#64;Override
public void eat() {
System.out.println("狗爱吃骨头&#xff01;&#xff01;&#xff01;");
}
}

执行结果

执行lua脚本

&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;执行完成&#61;&#61;&#61;&#61;&#61;&#61;
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>

由上可知&#xff0c;当程序执行异常时&#xff0c;lua脚本中的内容同样没有生效







推荐阅读
author-avatar
LeoWang
帅气鄙人的PHP程序员
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有