Spring Data Redis 教程

RedisTemplate 核心方法 execute

在 RedisTemplate 中,定义了几个 execute() 方法,这些方法是 RedisTemplate 的核心方法。RedisTemplate 中很多其他方法均是通过调用 execute 来执行具体的操作。例如:

/*
 * (non-Javadoc)
 * @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection)
 */
@Override
public Long delete(Collection<K> keys) {

   if (CollectionUtils.isEmpty(keys)) {
      return 0L;
   }

   byte[][] rawKeys = rawKeys(keys);

   return execute(connection -> connection.del(rawKeys), true);
}

上述方法是 RedisTemplate 中 delete 方法的源码,它就是使用 execute() 来执行具体的删除操作(即调用 connection.del(rawKeys) 方法)。

方法说明如下表:

方法定义方法说明
<T> T  execute(RedisCallback<T> action)在 Redis 连接中执行给定的操作
<T> T  execute(RedisCallback<T> action, boolean exposeConnection)在连接中执行给定的操作对象,可以公开也可以不公开。
<T> T  execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline)在可以公开或不公开的连接中执行给定的操作对象。
<T> T  execute(RedisScript<T> script, List<K> keys, Object... args)执行给定的 RedisScript
<T> T  execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args)执行给定的 RedisScript,使用提供的 RedisSerializers 序列化脚本参数和结果。
<T> T  execute(SessionCallback<T> session)执行 Redis 会话

示例

execute(RedisCallback) 简单用法

使用 RedisTemplate 直接调用 opsFor** 来操作 Redis 数据库,每执行一条命令是要重新拿一个连接,因此很耗资源。如果让一个连接直接执行多条语句的方法就是使用 RedisCallback(它太复杂,不常用),推荐使用 SessionCallback。

本例将演示使用 RedisCallback 向 Redis 写入数据,然后再将写入的数据取出来,输出到控制台。如下:

package com.hxstrive.redis.redistemplate.execute;

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.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ExecuteSimple {

    /** 注入 RedisTemplate */
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Test
    public void contextLoads() {
        redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisStringCommands commands = connection.stringCommands();
                // 写入缓存
                commands.set("execute_key".getBytes(), "hello world".getBytes());

                // 从缓存获取值
                byte[] value = commands.get("execute_key".getBytes());
                System.out.println(new String(value));

                return null;
            }
        });
    }

}

运行示例,输出结果如下:

hello world

其实,在 RedisTemplate 中,其他很多方法均是通过调用 execute() 方法来实现,只是不同的方法实现不同的回调接口。部分源码如下:

// ...
@Override
public Long increment(K key) {
   byte[] rawKey = rawKey(key);
   return execute(connection -> connection.incr(rawKey), true);
}

/*
 * (non-Javadoc)
 * @see org.springframework.data.redis.core.ValueOperations#increment(java.lang.Object, long)
 */
@Override
public Long increment(K key, long delta) {
   byte[] rawKey = rawKey(key);
   return execute(connection -> connection.incrBy(rawKey, delta), true);
}

@Override
public void set(K key, V value, long timeout, TimeUnit unit) {

   byte[] rawKey = rawKey(key);
   byte[] rawValue = rawValue(value);

   execute(new RedisCallback<Object>() {

      @Override
      public Object doInRedis(RedisConnection connection) throws DataAccessException {

         potentiallyUsePsetEx(connection);
         return null;
      }

      public void potentiallyUsePsetEx(RedisConnection connection) {

         if (!TimeUnit.MILLISECONDS.equals(unit) || !failsafeInvokePsetEx(connection)) {
            connection.setEx(rawKey, TimeoutUtils.toSeconds(timeout, unit), rawValue);
         }
      }

      private boolean failsafeInvokePsetEx(RedisConnection connection) {

         boolean failed = false;
         try {
            connection.pSetEx(rawKey, timeout, rawValue);
         } catch (UnsupportedOperationException e) {
            // in case the connection does not support pSetEx return false to allow fallback to other operation.
            failed = true;
         }
         return !failed;
      }

   }, true);
}
// ...

execute(SessionCallback) 简单用法

使用 RedisTemplate 直接调用 opsFor** 来操作 Redis 数据库,每执行一条命令是要重新拿一个连接,因此很耗资源。如果让一个连接直接执行多条语句的方法就是使用 SessionCallback,还可以使用 RedisCallback(它太复杂,不常用)。

package com.hxstrive.redis.redistemplate.execute;

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.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SessionCallbackSimple {

    /** 注入 RedisTemplate */
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Test
    public void contextLoads() {
        redisTemplate.execute(new SessionCallback() {
            @Override
            public String execute(RedisOperations operations) throws DataAccessException {
                operations.opsForValue().set("valueK1", "value1");
                System.out.println("valueK1 = " + operations.opsForValue().get("valueK1"));

                operations.opsForList().leftPushAll("listK1", "one", "two");
                System.out.println("listK1 = " + operations.opsForList().size("listK1"));
                return null;
            }
        });
    }

}

运行示例,输出如下:

valueK1 = value1
listK1 = 2

execute(RedisScript) 简单用法

该示例使用 Lua 脚本实现获取锁的功能,代码如下:

package com.hxstrive.redis.redistemplate.execute;

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.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Collections;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisScriptSimple {

    /** 注入 RedisTemplate */
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Test
    public void contextLoads() {
        // 使用 lua 脚本实现获取锁
        // ARGV[1] = lock-key
        // ARGV[2] = 30
        String script = "local key = ARGV[1];" +
                "local expiration = ARGV[2];" +
                "local value = 1;" +
                "if redis.call('EXISTS', key) == 1 then " +
                "  return -1 " + // 如果键存在,则返回-1
                "else " +
                "  redis.call('SET', key, value);" + // 如果键不存在,则设置键和值
                "  redis.call('EXPIRE', key, expiration);" + // 为键设置过期时间
                "  return 1;" + // 返回1,表示锁获取成功
                "end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        // 其中,lock-key 表示锁的键,30 表示过期时间为30秒
        Long val = redisTemplate.execute(redisScript, Collections.EMPTY_LIST, "lock-key", "30");
        System.out.println("val = " + val);

        // 如果锁获取成功,则进入下一步操作
        if(null != val && val == 1) {
            System.out.println("获取锁成功");
        } else {
            System.err.println("获取锁失败");
        }
    }

}

运行示例,输出如下:

val = 1
获取锁成功


说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号