springboot 和 redis事务整理

RedisTemplate是springboot对redis操作的封装

第一步就是介绍RedisTemplate

/**
     * @param redisConnectionFactory
     * @return 自定义redisTemplate,自带的bean没有序列化器
     */
    @Bean(name = "redisObjectTemplate")
    public RedisTemplate<Object, Object> redisObjectTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new RedisConverter());//设置key的序列化器
        redisTemplate.setValueSerializer(new RedisConverter());//设置值的序列化器
//        redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }
    /**
     * @param redisConnectionFactory
     * @return 自定义redisTemplate,自带的bean没有序列化器
     * LettuceConnectionFactory
     */
    @Bean(name = "redisStringTemplate")
    public RedisTemplate<String, Object> redisStringTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());//设置key的序列化器
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);//设置值的序列化器
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

//        redisTemplate.setEnableTransactionSupport(true);//支持事务
        redisTemplate.afterPropertiesSet();//非spring注入使用RedisTemplate,需先调用afterPropertiesSet()方法
        return redisTemplate;
    }

这里设置了二个不同的 RedisTemplate,序列化的方式有区别

RedisConverter为实现了RedisSerializer<Object> 的自定义序列化工具

LettuceConnectionFactory集群 RedisConnectionFactory单机 根据实际情况配置

redisTemplate.setEnableTransactionSupport(true);这个需要特殊注意,如果需要使用@Transaction的一定需要有这个配置

这里用的默认 RedisTemplate 用户也可以定义自己的 RedisTemplate,如下

public class RedisTemplateDelegate<K, V> extends RedisTemplate<K, V> {
	@Resource(name = "notSupportTransactionRedisTemplate")
	private RedisTemplate<K, V> notSupportTransactionRedisTemplate;
 
	@Override
	public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
		//判断是否有@Transactional注解,如果有就用支持事务的RedisTemplate
		if (TransactionSynchronizationManager.isActualTransactionActive()) {
			return super.execute(action, exposeConnection, pipeline);
		} else {
			return notSupportTransactionRedisTemplate.execute(action, exposeConnection, pipeline);
		}
	}
 
	@Override
	public <T> T execute(SessionCallback<T> session) {
		if (TransactionSynchronizationManager.isActualTransactionActive()) {
			return super.execute(session);
		} else {
			return notSupportTransactionRedisTemplate.execute(session);
		}
	}
 
	@Override
	public List<Object> executePipelined(final SessionCallback<?> session, final RedisSerializer<?> resultSerializer) {
		if (TransactionSynchronizationManager.isActualTransactionActive()) {
			return super.executePipelined(session, resultSerializer);
		} else {
			return notSupportTransactionRedisTemplate.executePipelined(session, resultSerializer);
		}
	}
}

第二步 : 如何使用 redisTemplate

bean中注入定义好的 redisTemplate

@Autowired
private RedisTemplate<String, Object> redisStringTemplate;

1、如果已经设置redisTemplate.setEnableTransactionSupport(true),直接使用@Transaction 系统在切面已经自动做了封装

特别需要注意  Transaction 方法中不能使用get去取值或者去watch(watch需要在事务之前),也不要去mutli和exec了(注解已经自动帮我们mutli了)。

如果在使用中不加@Transaction注解,会造成Redis连接不会释放的问题。

2、如果没有设置redisTemplate.setEnableTransactionSupport(true)

            redisStringTemplate.watch(Arrays.asList(amoutId, ordersId));

            redisStringTemplate.multi();
            amoutIdValue = (String)redisStringTemplate.opsForValue().get(amoutId);
            LogUtil.info(logger, "amoutIdValue:"+amoutIdValue);
            redisStringTemplate.opsForValue().set(amoutId, "300");
            redisStringTemplate.delete(ordersId);
            List<Object> res = redisStringTemplate.exec();
            if (res == null || res.isEmpty()) {
                LogUtil.info(logger, "redis失败");
            } else {
                LogUtil.info(logger, "redis成功");
            }

以上代码会报错 No ongoing transaction. Did you forget to call multi?

org.springframework.dao.InvalidDataAccessApiUsageException: No ongoing transaction. Did you forget to call multi?
	at org.springframework.data.redis.connection.jedis.JedisConnection.exec(JedisConnection.java:775)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:57)
	at com.sun.proxy.$Proxy143.exec(Unknown Source)

因为每次redisTemplate都会获取新的redis连接,所以推荐做法重写execute进行操作

          res = redisStringTemplate.execute(new SessionCallback<List<Object>>() {
                /**
                 * Executes all the given operations inside the same session.
                 *
                 * @return return value
                 */
                @Override
                public List<Object> execute(RedisOperations ro){
                    ro.watch(Arrays.asList(amoutId, ordersId));
                    ro.multi();
                    String amoutIdValue =                 
                         (String)redisStringTemplate.opsForValue().get(amoutId);
                    LogUtil.info(logger, "amoutIdValue:"+amoutIdValue);
                    ro.opsForValue().set(amoutId, "300");
                    ro.delete(ordersId);
                    List<Object> res = ro.exec();
                    ro.unwatch();
                    return res;
                }
            });
发布了20 篇原创文章 · 获赞 23 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/hdu09075340/article/details/103495066
今日推荐