栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

Redis 事务在 SpringBoot 中的应用

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Redis 事务在 SpringBoot 中的应用

环境信息
  • JDK 版本信息

  1.8

  • SpringBoot 版本、依赖信息

  org.springframework.boot
  spring-boot-starter-parent
  2.0.2.RELEASE
  

我们在 SpringBoot 中使用 Redis 时,会引入如下的 redis starter


  org.springframework.boot
  spring-boot-starter-data-redis

这个 starter 引入了 jedis 和 spring-data-redis 两个与 redis 核心的包。

Redis 事务相关的命令参考
  • 事务
Redis 事务在 SpringBoot 中的应用

说明:下面以测试用例的形式说明 Redis 事务在 SpringBoot 中正确与错误的用法。首先,看一看当前测试用例的主体代码:

package com.imooc.ad.service;

import com.imooc.ad.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class RedisTransTest {

    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
  • 错误的用法

@Test
public void testMultiFailure() {

  stringRedisTemplate.multi();
  stringRedisTemplate.opsForValue().set("name", "qinyi");
  stringRedisTemplate.opsForValue().set("gender", "male");
  stringRedisTemplate.opsForValue().set("age", "19");
  System.out.println(stringRedisTemplate.exec());
}

执行以上测试用例,会抛出如下的异常信息:

Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR EXEC without MULTI

这里给出的错误信息显示:在执行 EXEC 命令之前,没有执行 MULTI 命令。这很奇怪,我们明明在测试方法的第一句就执行了 MULTI。通过追踪 multi、exec 等方法,我们可以看到如下的执行源码(spring-data-redis):

public  T execute(RedisCallback action, boolean exposeConnection, boolean pipeline) {

  Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
  Assert.notNull(action, "Callback object must not be null");

  RedisConnectionFactory factory = getRequiredConnectionFactory();
  RedisConnection conn = null;
  try {
    // RedisTemplate 的 enableTransactionSupport 属性标识是否开启了事务支持,默认是 false
	   if (enableTransactionSupport) {
		     // only bind resources in case of potential transaction synchronization
		conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
	   } else {
		     conn = RedisConnectionUtils.getConnection(factory);
	   }

     boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);

源码中已经给出了答案:由于 enableTransactionSupport 属性的默认值是 false,导致了每一个 RedisConnection 都是重新获取的。所以,我们刚刚执行的 MULTI 和 EXEC 这两个命令不在同一个 Connection 中。

  • 设置 enableTransactionSupport 开启事务支持

解决上述示例的问题,最简单的办法就是让 RedisTemplate 开启事务支持,即设置 enableTransactionSupport 为 true 就可以了。测试代码如下:


@Test
public void testMultiSuccess() {
  // 开启事务支持,在同一个 Connection 中执行命令
  stringRedisTemplate.setEnableTransactionSupport(true);

  stringRedisTemplate.multi();
  stringRedisTemplate.opsForValue().set("name", "qinyi");
  stringRedisTemplate.opsForValue().set("gender", "male");
  stringRedisTemplate.opsForValue().set("age", "19");
  System.out.println(stringRedisTemplate.exec());     // [true, true, true]
}
  • 通过 SessionCallback,保证所有的操作都在同一个 Session 中完成

更常见的写法仍是采用 RedisTemplate 的默认配置,即不开启事务支持。但是,我们可以通过使用 SessionCallback,该接口保证其内部所有操作都是在同一个Session中。测试代码如下:


@Test
@SuppressWarnings("all")
public void testSessionCallback() {

    SessionCallback callback = new SessionCallback() {
 @Override
 public Object execute(RedisOperations operations) throws DataAccessException {
     operations.multi();
     operations.opsForValue().set("name", "qinyi");
     operations.opsForValue().set("gender", "male");
     operations.opsForValue().set("age", "19");
     return operations.exec();
 }
    };

    // [true, true, true]
    System.out.println(stringRedisTemplate.execute(callback));
}

总结:我们在 SpringBoot 中操作 Redis 时,使用 RedisTemplate 的默认配置已经能够满足大部分的场景了。如果要执行事务操作,使用 SessionCallback 是比较好,也是比较常用的选择。

·······························
欢迎关注课程:

基于 SpringCloud 微服务架构下 广告系统设计与实现

JAVA分布式优惠券系统后台 手把手实战开发

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号