默认情况下,RedisTemplate 不参与管理 Spring 事务。如果你想让 RedisTemplate 在使用 @Transactional 或 TransactionTemplate 时使用 Redis 事务,你需要通过设置 setEnableTransactionSupport(true) 明确地为每个RedisTemplate 启用事务支持。
启用事务支持将 RedisConnection 绑定到由 ThreadLocal 支持的当前事务。如果事务完成时没有错误,Redis 事务会以 EXEC 的方式提交,否则以 DISCARD 的方式回滚。Redis 事务是面向批处理的。在一个正在进行的事务中发出的命令是排队的,只有在提交事务时才应用。
Spring Data Redis 区分了正在进行的事务中的只读和写命令。只读命令,如 KEYS,被输送到一个新的(非线程绑定的)RedisConnection 以允许读取。写命令由 RedisTemplate 排队,在提交时应用。
下面的例子显示了如何配置事务管理:
@Configuration // 配置 Spring Context 以启用声明式事务管理 @EnableTransactionManagement public class RedisTxContextConfiguration { @Bean public StringRedisTemplate redisTemplate() { StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory()); // explicitly enable transaction support // 将 RedisTemplate 配置为通过将连接绑定到当前线程来参与事务 template.setEnableTransactionSupport(true); return template; } @Bean public RedisConnectionFactory redisConnectionFactory() { // jedis || Lettuce } @Bean public PlatformTransactionManager transactionManager() throws SQLException { // 事务管理需要一个 PlatformTransactionManager。 // Spring Data Redis 不提供 PlatformTransactionManager 的实现。 // 假设您的应用程序使用了 JDBC,SpringDataRedis 可以通过使用现有的事务管理器参与事务。 return new DataSourceTransactionManager(dataSource()); } @Bean public DataSource dataSource() throws SQLException { // ... } }
下面的例子分别展示了一个使用约束:
// 必须在线程绑定的连接上进行 template.opsForValue().set("thing1", "thing2"); // 读取操作必须在空闲(非事务感知)连接上运行 template.keys("*"); // 返回 null,因为事务中设置的值不可见 template.opsForValue().get("thing1");
由于 Spring Data Redis 不提供 PlatformTransactionManager 的实现。如果您的应用程序使用了 JDBC,Spring Data Redis 可以通过使用现有的事务管理器来参与事务。向 pom.xml 文件中添加如下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
package com.hxstrive.redis.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.*; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; import java.sql.SQLException; @Configuration // 1.配置 Spring 的声明式事务 @EnableTransactionManagement public class RedisConfigTransaction { @Bean public RedisTemplate<String,String> transactionRedisTemplate(RedisConnectionFactory factory) { RedisTemplate<String,String> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); // 设置键序列化方式 redisTemplate.setKeySerializer(new StringRedisSerializer()); // 设置简单类型值的序列化方式 redisTemplate.setValueSerializer(new StringRedisSerializer()); // 设置默认序列化方式 redisTemplate.setDefaultSerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); // 2.将 RedisTemplate 配置为通过将连接绑定到当前线程来参与事务 redisTemplate.setEnableTransactionSupport(true); return redisTemplate; } // 3.配置事务管理 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) throws SQLException { // 事务管理需要一个 PlatformTransactionManager。 // Spring Data Redis 不提供 PlatformTransactionManager 的实现。 // 假设您的应用程序使用了 JDBC,SpringDataRedis 可以通过使用现有的事务管理器参与事务。 return new DataSourceTransactionManager(dataSource); } }
使用 SessionCallback 实现 Redis 事务,如下:
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; 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.RedisTemplate; import org.springframework.data.redis.core.SessionCallback; /** * 事务示例 * @author hxstrive.com 2022/11/3 */ @SpringBootTest public class TransactionSessionCallbackDemo { @Autowired @Qualifier("transactionRedisTemplate") private RedisTemplate<String,String> redisTemplate; @BeforeEach public void init() { redisTemplate.delete(redisTemplate.keys("*")); } @Test public void sessionCallback() { redisTemplate.execute(new SessionCallback<Object>() { @Override public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException { // 开启一个事务 operations.multi(); // 执行多条语句 operations.opsForValue().set((K)"key1", (V)"value1"); operations.opsForValue().set((K)"key2", (V)"value2"); operations.opsForValue().set((K)"key3", (V)"value3"); // 提交执行事务,返回结果:[true, true, true] System.out.println(operations.exec()); return null; } }); } }
使用 RedisTemplate 类的 multi() 和 exec() 方法实现事务,如下:
package com.hxstrive.redis.transaction; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.transaction.annotation.Transactional; /** * 事务示例 * @author hxstrive.com 2022/11/3 */ @SpringBootTest public class TransactionRedisTemplateDemo { @Autowired @Qualifier("transactionRedisTemplate") private RedisTemplate<String,String> redisTemplate; @BeforeEach public void init() { redisTemplate.delete(redisTemplate.keys("*")); } // 使用该注解开启事务 @Transactional(rollbackFor = Exception.class) @Test public void redisTemplate() { redisTemplate.multi(); redisTemplate.opsForValue().set("key1", "value1"); redisTemplate.opsForValue().set("key2", "value2"); redisTemplate.opsForValue().set("key3", "value3"); System.out.println(redisTemplate.exec()); } }