Spring boot集成redis使用事务,以及事务失效的因为

文章讲述了在SpringBoot应用中,从Jedis切换到使用redisTemplate时遇到的事务问题。当尝试使用Redis的事务功能时,出现错误ERRDISCARDwithoutMULTI。问题源于redisTemplate在事务结束时会关闭连接。为了解决这个问题,文章推荐使用redisTemplate的execute方法来处理事务,并提供了示例代码展示有效和无效的事务处理方式。此外,还提到了配置redisTemplate支持事务及序列化的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.依赖

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.9-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

2.事务使用报错

  • 从jedis换到redisTemplate,使用事务会出现以下错误:
Caused by: io.lettuce.core.RedisCommandExecutionException: ERR DISCARD without MULTI
  • 因为redisTemplate使用事务命令multi之后discard命令和exec命令二选一,因为不管执行哪一个代码最后都会关闭当前的连接,看源码:
public static void unbindConnection(RedisConnectionFactory factory) {

		RedisConnectionHolder conHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);

		if (conHolder == null) {
			return;
		}

		if (log.isDebugEnabled()) {
			log.debug("Unbinding Redis Connection");
		}

		if (conHolder.isTransactionActive()) {
			if (log.isDebugEnabled()) {
				log.debug("Redis Connection will be closed when outer transaction finished");
			}
		} else {

			RedisConnection connection = conHolder.getConnection();
			conHolder.released();

			if (!conHolder.isOpen()) {

				TransactionSynchronizationManager.unbindResourceIfPossible(factory);

				doCloseConnection(connection);   //关闭连接
			}
		}
	}

3.事务回滚

redis配置:以下使用事务需要注意两点第一点是开启支持事务,以及事务的执行建议使用redisTemplate的execute方法实现接口,因为官网建议的spring官网https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#tx.spring

/**
     * 序列化相关的配置
     */
    @Bean("myRedisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory  factory) {
        RedisTemplate<String, Object> redisTemplate  = new RedisTemplate<>();
        //支持事务
        redisTemplate.setEnableTransactionSupport(true);

        //json方式序列化
        Jackson2JsonRedisSerializer jackson = new Jackson2JsonRedisSerializer<>(Object.class);
        redisTemplate.setConnectionFactory(factory);
        //String方式序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        //配置序列化的方式不用设置
        /*redisTemplate.setDefaultSerializer();
        redisTemplate.setStringSerializer();
        redisTemplate.setEnableDefaultSerializer();*/

        //设置所有的key为string方式序列化
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        //设置所有的value为jackson方式序列化
        redisTemplate.setValueSerializer(jackson);
        redisTemplate.setHashValueSerializer(jackson);

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

无效事务,在jedis,以下是可以回滚的

 redisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                List<Object> exec = null;
                try {
                    operations.multi();

                    operations.opsForValue().set((K) "username", (V) "wyx");

                    operations.opsForSet().members((K) "username");   //用set类型获取string类型的值,

                    exec = operations.exec();
                } catch (Exception e) {
                    redisTemplate.discard();
                }
                return exec;
            }
        });
    }

有效事务

 redisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                List<Object> exec = null;
                try {
                    operations.multi();

                    operations.opsForValue().set((K) "username", (V) "wyx");

                    operations.opsForSet().members((K) "username");   //用set类型获取string类型的值,
                    System.out.println(1/0);  //redis命令使用的错误将不会回滚
                    exec = operations.exec();
                } catch (Exception e) {
                    redisTemplate.discard();  
                }
                return exec;
            }
        });
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值