|
@@ -0,0 +1,995 @@
|
|
|
|
+package com.persagy.apm.common.helper;
|
|
|
|
+
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
+import org.springframework.dao.DataAccessException;
|
|
|
|
+import org.springframework.data.redis.connection.RedisConnection;
|
|
|
|
+import org.springframework.data.redis.core.RedisCallback;
|
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
|
+import org.springframework.data.redis.core.SessionCallback;
|
|
|
|
+import org.springframework.data.redis.core.script.DefaultRedisScript;
|
|
|
|
+import org.springframework.data.redis.serializer.GenericToStringSerializer;
|
|
|
|
+import org.springframework.data.redis.serializer.RedisSerializer;
|
|
|
|
+
|
|
|
|
+import java.io.Serializable;
|
|
|
|
+import java.util.*;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Redis工具类
|
|
|
|
+ * @author Charlie Yu
|
|
|
|
+ * @version 1.0 2021-03-04
|
|
|
|
+ */
|
|
|
|
+@Slf4j
|
|
|
|
+public final class RedisHelper {
|
|
|
|
+
|
|
|
|
+ /** jedis模板 */
|
|
|
|
+ private static RedisTemplate redisTemplate = null;
|
|
|
|
+
|
|
|
|
+ /** 助手类,私有构造方法 */
|
|
|
|
+ private RedisHelper() {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取缓存模板
|
|
|
|
+ * @return 获取缓存模板
|
|
|
|
+ */
|
|
|
|
+ public static RedisTemplate getTemplate() {
|
|
|
|
+ if (redisTemplate == null) {
|
|
|
|
+ redisTemplate = SpringHelper.getBean("redisTemplate", RedisTemplate.class);
|
|
|
|
+ if (redisTemplate == null) {
|
|
|
|
+ Map<String, RedisTemplate> beans = SpringHelper.getBeansOfType(RedisTemplate.class);
|
|
|
|
+ if (!beans.isEmpty()) {
|
|
|
|
+ redisTemplate = beans.values().iterator().next();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (redisTemplate == null) {
|
|
|
|
+ log.error("redis config is error! please init RedisTemplate bean!");
|
|
|
|
+ throw new RuntimeException("redis config is error! please init RedisTemplate bean!");
|
|
|
|
+ }
|
|
|
|
+ return redisTemplate;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值模式删除指定键值
|
|
|
|
+ * @param keyMode 键值匹配模式
|
|
|
|
+ * @return 清楚记录数组
|
|
|
|
+ */
|
|
|
|
+ public static long clearKeys(String keyMode) {
|
|
|
|
+ String clearScript = "local keys = redis.call('keys', ARGV[1]) for i=1,#keys,5000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys";
|
|
|
|
+ return (long) getTemplate().execute(new DefaultRedisScript<>(clearScript, Long.class), getKeySerializer(), getValueSerializer(), new ArrayList(), keyMode);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值模式删除指定键值
|
|
|
|
+ * @param keys 键值列表
|
|
|
|
+ * @return 清楚记录数
|
|
|
|
+ */
|
|
|
|
+ public static long clearKeys(List<String> keys) {
|
|
|
|
+ String clearScript = "return redis.call('del', unpack(KEY))";
|
|
|
|
+ return (long) getTemplate().execute(new DefaultRedisScript<>(clearScript, Long.class), getKeySerializer(), getValueSerializer(), keys, new Object[0]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 指定键值累加操作
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 累加结果
|
|
|
|
+ */
|
|
|
|
+ public static Long incr(final String key) {
|
|
|
|
+ return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.incr(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 指定键值累减操作
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 累减结果
|
|
|
|
+ */
|
|
|
|
+ public static Long decr(final String key) {
|
|
|
|
+ return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.decr(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值获取存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> T cmdGet(final String key, Class<T> cl) {
|
|
|
|
+ return strDeserial(cl, getBytes(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值获取存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> T get(String key) {
|
|
|
|
+ return objDeserial(getBytes(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值获取存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static byte[] getBytes(final byte[] key) {
|
|
|
|
+ return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.get(key));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值存储新的值并获取旧存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 新存储值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 旧存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> T cmdGetAndSet(String key, T value) {
|
|
|
|
+ Class<T> cl = (Class<T>) value.getClass();
|
|
|
|
+ return strDeserial(cl, getAndSetBytes(keySerial(key), strSerial(value)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值存储新的值并获取旧存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 新存储值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 旧存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> T getAndSet(String key, T value) {
|
|
|
|
+ return objDeserial(getAndSetBytes(keySerial(key), objSerial(value)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值存储新的值并获取旧存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 新存储值
|
|
|
|
+ * @return 旧存储值
|
|
|
|
+ */
|
|
|
|
+ public static byte[] getAndSetBytes(final byte[] key, final byte[] value) {
|
|
|
|
+ return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.getSet(key, value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void cmdSet(String key, T value) {
|
|
|
|
+ setBytes(keySerial(key), strSerial(value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void set(String key, T value) {
|
|
|
|
+ setBytes(keySerial(key), objSerial(value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static <T extends Serializable> Boolean setNx(String key, T value) {
|
|
|
|
+ return setNxBytes(keySerial(key), objSerial(value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static Boolean setNxBytes(final byte[] key, final byte[] value) {
|
|
|
|
+ return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.setNX(key, value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static <T extends Serializable> Boolean setNxEx(String key, T value, final int timeout) {
|
|
|
|
+ return setExNxBytes(keySerial(key), objSerial(value), timeout);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static Boolean setExNxBytes(final byte[] key, final byte[] value, final int timeout) {
|
|
|
|
+ return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> {
|
|
|
|
+ Boolean success = connection.setNX(key, value);
|
|
|
|
+ if (success != null && success) {
|
|
|
|
+ connection.expire(key, timeout);
|
|
|
|
+ }
|
|
|
|
+ return success;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 按键值存储数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ */
|
|
|
|
+ public static void setBytes(final byte[] key, final byte[] value) {
|
|
|
|
+ getTemplate().execute((RedisCallback<Void>) connection -> {
|
|
|
|
+ connection.set(key, value);
|
|
|
|
+ return null;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储一定时间内过期的值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ * @param timeout 超时时长
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void cmdSetEx(final String key, final T value, final int timeout) {
|
|
|
|
+ setExBytes(keySerial(key), strSerial(value), timeout);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储一定时间内过期的值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ * @param timeout 超时时长
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void setEx(final String key, final T value, final int timeout) {
|
|
|
|
+ setExBytes(keySerial(key), objSerial(value), timeout);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储一定时间内过期的值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ * @param timeout 超时时长
|
|
|
|
+ */
|
|
|
|
+ public static void setExBytes(final byte[] key, final byte[] value, final int timeout) {
|
|
|
|
+ getTemplate().execute((RedisCallback<Void>) connection -> {
|
|
|
|
+ connection.setEx(key, timeout, value);
|
|
|
|
+ return null;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 为指定键值增加过期时长
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param timeout 超时时长
|
|
|
|
+ */
|
|
|
|
+ public static boolean expire(final String key, final int timeout) {
|
|
|
|
+ return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.expire(keySerial(key), timeout));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 为指定键值增加有效期(日期时间)
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param date 有效期
|
|
|
|
+ */
|
|
|
|
+ public static boolean expireAt(final String key, final long date) {
|
|
|
|
+ return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.expireAt(keySerial(key), date));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断键值还有多少秒超时
|
|
|
|
+ * @param key 键值
|
|
|
|
+ */
|
|
|
|
+ public static long ttl(final String key) {
|
|
|
|
+ return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.ttl(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断指定键值是否存在
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 布尔值
|
|
|
|
+ */
|
|
|
|
+ public static boolean exists(final String key) {
|
|
|
|
+ return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.exists(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 删除指定键值数据
|
|
|
|
+ * @param keys 键值数组
|
|
|
|
+ * @return 删除的个数
|
|
|
|
+ */
|
|
|
|
+ public static long del(final String... keys) {
|
|
|
|
+ if (keys == null || keys.length == 0) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.del(fields(keys)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取指定的hash存储的值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param field hash键值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> T cmdHGet(String key, Class<T> cl, String field) {
|
|
|
|
+ return strDeserial(cl, hGetBytes(keySerial(key), keySerial(field)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取指定的hash存储的值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param field hash键值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> T hGet(String key, String field) {
|
|
|
|
+ return objDeserial(hGetBytes(keySerial(key), keySerial(field)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取指定的hash存储的值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param field hash键值
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static byte[] hGetBytes(final byte[] key, final byte[] field) {
|
|
|
|
+ return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.hGet(key, field));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取多个hash存储的值,由于值类型不同,不做反序列化
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param fields 多个hash键值
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static List<byte[]> hMGet(final String key, final String... fields) {
|
|
|
|
+ return (List<byte[]>) getTemplate().execute((RedisCallback<List<byte[]>>) connection -> connection.hMGet(keySerial(key), fields(fields)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取多个hash存储的值,值类型相同时使用
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param fields 多个hash键值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> List<T> hMGetSameType(Class<T> cl, String key, String... fields) {
|
|
|
|
+ return strListDeserial(cl, hMGet(key, fields));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取多个hash存储的值,值类型相同时使用
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param fields 多个hash键值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ * @return 存储值
|
|
|
|
+ */
|
|
|
|
+ public static <T> List<T> hMGetSameType(String key, String... fields) {
|
|
|
|
+ return objListDeserial(hMGet(key, fields));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断指定的hash存储是否存在
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param field hash键值
|
|
|
|
+ * @return 布尔值
|
|
|
|
+ */
|
|
|
|
+ public static Boolean hExists(final String key, final String field) {
|
|
|
|
+ return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.hExists(keySerial(key), keySerial(field)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取整个hash存储,由于值类型不同,不做反序列化
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 存储的数据映射
|
|
|
|
+ */
|
|
|
|
+ public static List<String> hKeys(final String key) {
|
|
|
|
+ return (List<String>) getTemplate().execute((RedisCallback<List<String>>) connection -> strListDeserial(String.class, connection.hKeys(keySerial(key))));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取整个hash存储,由于值类型不同,不做反序列化
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 存储的数据映射
|
|
|
|
+ */
|
|
|
|
+ public static Map<byte[], byte[]> hGetAllBytes(final String key) {
|
|
|
|
+ return (Map<byte[], byte[]>) getTemplate().execute((RedisCallback<Map<byte[], byte[]>>) connection -> connection.hGetAll(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取整个hash存储,由于值类型不同,不做反序列化
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 存储的数据映射
|
|
|
|
+ */
|
|
|
|
+ public static Map<String, byte[]> hGetAll(String key) {
|
|
|
|
+ return mapKeyDeserial(hGetAllBytes(key));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取整个hash存储,值类型相同时使用
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> hash存储值泛型
|
|
|
|
+ * @return 存储的数据映射
|
|
|
|
+ */
|
|
|
|
+ public static <T> Map<String, T> hGetAllSameType(String key, Class<T> cl) {
|
|
|
|
+ return strMapDeserial(cl, hGetAllBytes(key));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取整个hash存储,值类型相同时使用
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> hash存储值泛型
|
|
|
|
+ * @return 存储的数据映射
|
|
|
|
+ */
|
|
|
|
+ public static <T> Map<String, T> hGetAllSameType(final String key) {
|
|
|
|
+ return objMapDeserial(hGetAllBytes(key));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储单个hash值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param field hash存储
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void cmdHSet(final String key, final String field, final T value) {
|
|
|
|
+ hSetBytes(key, field, strSerial(value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储单个hash值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param field hash存储
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void hSet(final String key, final String field, final T value) {
|
|
|
|
+ hSetBytes(key, field, objSerial(value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储单个hash值
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param field hash存储
|
|
|
|
+ * @param value 存储值
|
|
|
|
+ */
|
|
|
|
+ public static void hSetBytes(final String key, final String field, final byte[] value) {
|
|
|
|
+ getTemplate().execute((RedisCallback<Boolean>) connection -> connection.hSet(keySerial(key), keySerial(field), value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * hash存储整个map
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param map 存储map
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void cmdHMSet(final String key, final Map<String, T> map) {
|
|
|
|
+ hMSetBytes(key, strMapSerial(map));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * hash存储整个map
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param map 存储map
|
|
|
|
+ * @param <T> 存储值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T extends Serializable> void hMSet(final String key, final Map<String, T> map) {
|
|
|
|
+ hMSetBytes(key, objMapSerial(map));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * hash存储整个map
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param map 存储map
|
|
|
|
+ */
|
|
|
|
+ public static void hMSetBytes(final String key, final Map<byte[], byte[]> map) {
|
|
|
|
+ getTemplate().execute((RedisCallback<Object>) connection -> {
|
|
|
|
+ connection.hMSet(keySerial(key), map);
|
|
|
|
+ return null;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 删除指定hash数据
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param fields hash键值
|
|
|
|
+ * @return 删除的数量
|
|
|
|
+ */
|
|
|
|
+ public static long hDel(final String key, final String... fields) {
|
|
|
|
+ if (StringUtils.isEmpty(key) || fields == null || fields.length == 0) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.hDel(keySerial(key), fields(fields)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set获取所有成员
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> List<T> sMemebers(String key, Class<T> cl) {
|
|
|
|
+ return strListDeserial(cl, sMemebersBytesList(key));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set获取所有成员
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> List<T> sMemebers(String key) {
|
|
|
|
+ return objListDeserial(sMemebersBytesList(key));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set获取所有成员
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static Set<byte[]> sMemebersBytesList(final String key) {
|
|
|
|
+ return (Set<byte[]>) getTemplate().execute((RedisCallback<Set<byte[]>>) connection -> connection.sMembers(keySerial(key)));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断是否为set成员
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> boolean cmdSIsMemeber(String key, T value) {
|
|
|
|
+ return sIsMemeber(keySerial(key), strSerial(value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断是否为set成员
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> boolean sIsMemeber(String key, T value) {
|
|
|
|
+ return sIsMemeber(keySerial(key), objSerial(value));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断是否为set成员
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static boolean sIsMemeber(final byte[] key, final byte[] value) {
|
|
|
|
+ return (Boolean) getTemplate().execute(new RedisCallback<Boolean>() {
|
|
|
|
+ @Override
|
|
|
|
+ public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
|
|
|
|
+ return connection.sIsMember(key, value);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断是否为set成员
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> boolean cmdSContainMemeber(final String key, final T... values) {
|
|
|
|
+ if (values == null || values.length == 0) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return sContainMemeber(keySerial(key), strArraySerial(values));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断是否为set成员
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> boolean sContainMemeber(final String key, final T... values) {
|
|
|
|
+ if (values == null || values.length == 0) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return sContainMemeber(keySerial(key), objArraySerial(values));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 判断是否为set成员
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 值列表
|
|
|
|
+ */
|
|
|
|
+ public static boolean sContainMemeber(byte[] key, byte[][] values) {
|
|
|
|
+ String sContainMemeberScript = "local vs = ARGV[1] for i=1,#vs,1 do if (redis.call('SISMEMBER',KEY[1], vs[i]) == 1) then return true end end return false";
|
|
|
|
+ ArrayList<byte[]> keys = new ArrayList<>();
|
|
|
|
+ keys.add(key);
|
|
|
|
+ Object[] objValues = values;
|
|
|
|
+ return (Boolean) getTemplate().execute(new DefaultRedisScript<>(sContainMemeberScript, Boolean.class), keys, objValues);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set添加
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T> long cmdSAdd(final String key, final T... values) {
|
|
|
|
+ return sAdd(keySerial(key), strArraySerial(values));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set添加
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T> long sAdd(final String key, final T... values) {
|
|
|
|
+ return sAdd(keySerial(key), objArraySerial(values));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set添加
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ */
|
|
|
|
+ public static long sAdd(final byte[] key, final byte[][] values) {
|
|
|
|
+ return (long) getTemplate().execute(new RedisCallback<Long>() {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Long doInRedis(RedisConnection connection) throws DataAccessException {
|
|
|
|
+ return connection.sAdd(key, values);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set添加所有的
|
|
|
|
+ *
|
|
|
|
+ * @param keys 键值组
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T> long cmdSAddAll(final List<String> keys, final List<T[]> values) {
|
|
|
|
+ return (long) getTemplate().execute(new RedisCallback<Long>() {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Long doInRedis(RedisConnection connection) throws DataAccessException {
|
|
|
|
+ long count = 0;
|
|
|
|
+ for (int i = 0; i < keys.size(); i++) {
|
|
|
|
+ connection.sAdd(keySerial(keys.get(i)), strArraySerial(values.get(i)));
|
|
|
|
+ }
|
|
|
|
+ return count;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set添加所有的
|
|
|
|
+ *
|
|
|
|
+ * @param keys
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T> long sAddAll(final List<String> keys, final List<T[]> values) {
|
|
|
|
+ return (long) getTemplate().execute(new RedisCallback<Long>() {
|
|
|
|
+ @Override
|
|
|
|
+ public Long doInRedis(RedisConnection connection) throws DataAccessException {
|
|
|
|
+ long count = 0;
|
|
|
|
+ for (int i = 0; i < keys.size(); i++) {
|
|
|
|
+ connection.sAdd(keySerial(keys.get(i)), objArraySerial(values.get(i)));
|
|
|
|
+ }
|
|
|
|
+ return count;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set删除
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T> long cmdSRem(final String key, final T... values) {
|
|
|
|
+ return sRem(keySerial(key), strArraySerial(values));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set删除
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ */
|
|
|
|
+ public static <T> long sRem(final String key, final T... values) {
|
|
|
|
+ return sRem(keySerial(key), objArraySerial(values));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * set删除
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @param values 值数组
|
|
|
|
+ */
|
|
|
|
+ public static long sRem(final byte[] key, final byte[]... values) {
|
|
|
|
+ return (Long) getTemplate().execute(new RedisCallback<Long>() {
|
|
|
|
+ @Override
|
|
|
|
+ public Long doInRedis(RedisConnection connection) throws DataAccessException {
|
|
|
|
+ return connection.sRem(key, values);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 事务模式执行redis操作
|
|
|
|
+ *
|
|
|
|
+ * @param callback 回调方法
|
|
|
|
+ * @return 返回结果
|
|
|
|
+ */
|
|
|
|
+ public static <T> T execute(SessionCallback<T> callback) {
|
|
|
|
+ return (T) getTemplate().execute(callback);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 键值序列化
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值
|
|
|
|
+ * @return 结果字节数组
|
|
|
|
+ */
|
|
|
|
+ public static byte[] keySerial(String key) {
|
|
|
|
+ return getKeySerializer().serialize(key);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 键值反序列化
|
|
|
|
+ *
|
|
|
|
+ * @param key 键值字节数组
|
|
|
|
+ * @return 键值
|
|
|
|
+ */
|
|
|
|
+ public static String keyDeserial(byte[] key) {
|
|
|
|
+ return (String) getKeySerializer().deserialize(key);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 多个键值序列化
|
|
|
|
+ *
|
|
|
|
+ * @param fields 键值数据
|
|
|
|
+ * @return 多个键值的字节矩阵
|
|
|
|
+ */
|
|
|
|
+ public static byte[][] fields(String... fields) {
|
|
|
|
+ RedisSerializer<String> s = getKeySerializer();
|
|
|
|
+ int len = fields.length;
|
|
|
|
+ byte[][] fs = new byte[len][];
|
|
|
|
+ for (int i = 0; i < fields.length; i++) {
|
|
|
|
+ fs[i] = s.serialize(fields[i]);
|
|
|
|
+ }
|
|
|
|
+ return fs;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 取得值redis序列化对象
|
|
|
|
+ *
|
|
|
|
+ * @return redis序列化对象
|
|
|
|
+ */
|
|
|
|
+ public static <T> RedisSerializer<T> getValueSerializer(Class<T> type) {
|
|
|
|
+ return new GenericToStringSerializer<>(type);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 取得值redis序列化对象
|
|
|
|
+ *
|
|
|
|
+ * @return redis序列化对象
|
|
|
|
+ */
|
|
|
|
+ public static <T> RedisSerializer<T> getValueSerializer() {
|
|
|
|
+ return getTemplate().getValueSerializer();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 取得键redis序列化对象
|
|
|
|
+ *
|
|
|
|
+ * @return redis序列化对象
|
|
|
|
+ */
|
|
|
|
+ public static <T> RedisSerializer<T> getKeySerializer() {
|
|
|
|
+ return getTemplate().getStringSerializer();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储值的序列化
|
|
|
|
+ *
|
|
|
|
+ * @param v 值对象
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 结果字节数组
|
|
|
|
+ */
|
|
|
|
+ public static <T> byte[] strSerial(T v) {
|
|
|
|
+ Class<T> c = (Class<T>) v.getClass();
|
|
|
|
+ return getValueSerializer(c).serialize(v);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储值的序列化
|
|
|
|
+ *
|
|
|
|
+ * @param v 值对象
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 结果字节数组
|
|
|
|
+ */
|
|
|
|
+ public static <T> byte[] objSerial(T v) {
|
|
|
|
+ return getValueSerializer().serialize(v);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储值的反序列化
|
|
|
|
+ *
|
|
|
|
+ * @param cl 结果对象类型
|
|
|
|
+ * @param v 值字节数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值对象
|
|
|
|
+ */
|
|
|
|
+ public static <T> T strDeserial(Class<T> cl, byte[] v) {
|
|
|
|
+ return getValueSerializer(cl).deserialize(v);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 存储值的反序列化
|
|
|
|
+ *
|
|
|
|
+ * @param v 值字节数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值对象
|
|
|
|
+ */
|
|
|
|
+ public static <T> T objDeserial(byte[] v) {
|
|
|
|
+ return (T) getValueSerializer().deserialize(v);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 反序列化值列表
|
|
|
|
+ *
|
|
|
|
+ * @param bs 存储值的字节数组列表
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值对象列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> List<T> strListDeserial(Class<T> cl, Collection<byte[]> bs) {
|
|
|
|
+ List<T> l = new ArrayList<>();
|
|
|
|
+ RedisSerializer<T> ser = getValueSerializer(cl);
|
|
|
|
+ for (byte[] b : bs) {
|
|
|
|
+ l.add(ser.deserialize(b));
|
|
|
|
+ }
|
|
|
|
+ return l;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 反序列化值列表
|
|
|
|
+ *
|
|
|
|
+ * @param bs 存储值的字节数组列表
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 值对象列表
|
|
|
|
+ */
|
|
|
|
+ public static <T> List<T> objListDeserial(Collection<byte[]> bs) {
|
|
|
|
+ List<T> l = new ArrayList<>();
|
|
|
|
+ RedisSerializer<T> ser = getValueSerializer();
|
|
|
|
+ for (byte[] b : bs) {
|
|
|
|
+ l.add(ser.deserialize(b));
|
|
|
|
+ }
|
|
|
|
+ return l;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 序列化map数据
|
|
|
|
+ *
|
|
|
|
+ * @param m map对象
|
|
|
|
+ * @param <T> map中值的泛型
|
|
|
|
+ * @return 字节到字节映射
|
|
|
|
+ */
|
|
|
|
+ public static <T> Map<byte[], byte[]> strMapSerial(Map<String, T> m) {
|
|
|
|
+ Map<byte[], byte[]> bs = new HashMap<>(16);
|
|
|
|
+ RedisSerializer<T> serializer = null;
|
|
|
|
+ for (Map.Entry<String, T> e : m.entrySet()) {
|
|
|
|
+ T v = e.getValue();
|
|
|
|
+ if (v == null) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ if (serializer == null) {
|
|
|
|
+ serializer = getValueSerializer((Class<T>) v.getClass());
|
|
|
|
+ }
|
|
|
|
+ bs.put(keySerial(e.getKey()), serializer.serialize(v));
|
|
|
|
+ }
|
|
|
|
+ return bs;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 序列化map数据
|
|
|
|
+ *
|
|
|
|
+ * @param m map对象
|
|
|
|
+ * @param <T> map中值的泛型
|
|
|
|
+ * @return 字节到字节映射
|
|
|
|
+ */
|
|
|
|
+ public static <T> Map<byte[], byte[]> objMapSerial(Map<String, T> m) {
|
|
|
|
+ Map<byte[], byte[]> bs = new HashMap<>(16);
|
|
|
|
+ RedisSerializer<T> serializer = getValueSerializer();
|
|
|
|
+ for (Map.Entry<String, T> e : m.entrySet()) {
|
|
|
|
+ T v = e.getValue();
|
|
|
|
+ if (v == null) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ bs.put(keySerial(e.getKey()), serializer.serialize(v));
|
|
|
|
+ }
|
|
|
|
+ return bs;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 反序列化字节到字节映射的键值
|
|
|
|
+ *
|
|
|
|
+ * @param bs 字节到字节映射
|
|
|
|
+ * @return 字符串到字节数据的映射
|
|
|
|
+ */
|
|
|
|
+ public static Map<String, byte[]> mapKeyDeserial(Map<byte[], byte[]> bs) {
|
|
|
|
+ Map<String, byte[]> m = new HashMap<>(16);
|
|
|
|
+ for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
|
|
|
|
+ m.put(keyDeserial(e.getKey()), e.getValue());
|
|
|
|
+ }
|
|
|
|
+ return m;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 反序列化字节到字节映射数据
|
|
|
|
+ *
|
|
|
|
+ * @param bs 字节到字节映射
|
|
|
|
+ * @param <T> map值泛型
|
|
|
|
+ * @return map数据对象
|
|
|
|
+ */
|
|
|
|
+ public static <T> Map<String, T> strMapDeserial(Class<T> cl, Map<byte[], byte[]> bs) {
|
|
|
|
+ Map<String, T> m = new HashMap<>(16);
|
|
|
|
+ RedisSerializer<T> ser = getValueSerializer(cl);
|
|
|
|
+ for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
|
|
|
|
+ m.put(keyDeserial(e.getKey()), ser.deserialize(e.getValue()));
|
|
|
|
+ }
|
|
|
|
+ return m;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 反序列化字节到字节映射数据
|
|
|
|
+ *
|
|
|
|
+ * @param bs 字节到字节映射
|
|
|
|
+ * @param <T> map值泛型
|
|
|
|
+ * @return map数据对象
|
|
|
|
+ */
|
|
|
|
+ public static <T> Map<String, T> objMapDeserial(Map<byte[], byte[]> bs) {
|
|
|
|
+ Map<String, T> m = new HashMap<>(16);
|
|
|
|
+ RedisSerializer<T> ser = getValueSerializer();
|
|
|
|
+ for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
|
|
|
|
+ m.put(keyDeserial(e.getKey()), ser.deserialize(e.getValue()));
|
|
|
|
+ }
|
|
|
|
+ return m;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 序列化数组
|
|
|
|
+ *
|
|
|
|
+ * @param vs 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 字节二维数组
|
|
|
|
+ */
|
|
|
|
+ public static <T> byte[][] strArraySerial(T... vs) {
|
|
|
|
+ byte[][] bss = new byte[vs.length][];
|
|
|
|
+ RedisSerializer<T> ser = null;
|
|
|
|
+ for (int i = 0; i < vs.length; i++) {
|
|
|
|
+ T v = vs[i];
|
|
|
|
+ if (v == null) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ if (ser == null) {
|
|
|
|
+ ser = getValueSerializer((Class<T>) v.getClass());
|
|
|
|
+ }
|
|
|
|
+ bss[i] = ser.serialize(v);
|
|
|
|
+ }
|
|
|
|
+ return bss;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 序列化数组
|
|
|
|
+ *
|
|
|
|
+ * @param vs 值数组
|
|
|
|
+ * @param <T> 值泛型
|
|
|
|
+ * @return 字节二维数组
|
|
|
|
+ */
|
|
|
|
+ public static <T> byte[][] objArraySerial(T... vs) {
|
|
|
|
+ byte[][] bss = new byte[vs.length][];
|
|
|
|
+ RedisSerializer<T> ser = getValueSerializer();
|
|
|
|
+ for (int i = 0; i < vs.length; i++) {
|
|
|
|
+ T v = vs[i];
|
|
|
|
+ if (v == null) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ bss[i] = ser.serialize(v);
|
|
|
|
+ }
|
|
|
|
+ return bss;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|