Redis存储List<T>集合

本文介绍如何将Spring框架与Redis数据库进行整合,包括配置连接池、实现基本的CRUD操作及如何存储和读取List集合等内容。

spring与redis整合

配置文件:

<!-- 连接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大连接数 -->
        <property name="maxTotal" value="30" />
        <!-- 最大空闲连接数 -->
        <property name="maxIdle" value="10" />
        <!-- 每次释放连接的最大数目 -->
        <property name="numTestsPerEvictionRun" value="1024" />
        <!-- 释放连接的扫描间隔(毫秒) -->
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <!-- 连接最小空闲时间 -->
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
        <property name="maxWaitMillis" value="1500" />
        <!-- 在获取连接的时候检查有效性, 默认false -->
        <property name="testOnBorrow" value="true" />
        <!-- 在空闲时检查有效性, 默认false -->
        <property name="testWhileIdle" value="true" />
        <!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
        <property name="blockWhenExhausted" value="false" />
    </bean> 
    <!-- jedis客户端单机版 -->
    <bean id="redisClient" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="host" value="127.0.0.1"></constructor-arg>
        <constructor-arg name="port" value="6379"></constructor-arg>
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
    </bean>

封装redis操作类JedisClientSingle :

public class JedisClientSingle {

    @Autowired
    private JedisPool jedisPool;

    public String get(String key) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.get(key);
        jedis.close();
        return string;
    }


    public String set(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.set(key, value);
        jedis.close();
        return string;
    }

    public String hget(String hkey, String key) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.hget(hkey, key);
        jedis.close();
        return string;
    }


    public long hset(String hkey, String key, String value) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hset(hkey, key, value);
        jedis.close();
        return result;
    }


    public long incr(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.incr(key);
        jedis.close();
        return result;
    }


    public long expire(String key, int second) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.expire(key, second);
        jedis.close();
        return result;
    }


    public long ttl(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.ttl(key);
        jedis.close();
        return result;
    }

}

存储List集合

测试:

@Autowired
private JedisClientSingle jedisClient;


//查询数据放入list集合,TaskManage实体类    
List<TaskManage> list = taskManageDao.findList(taskManage);
//向缓存中添加内容
try {
    //把list转换成字符串
    //向缓存中存入数据

    jedisClient.hset("qqqq:", 11 + "", JSON.toJSON(list).toString());

    } catch (Exception e) {

    e.printStackTrace();

    }

//从缓存中取内容
try {

    //获取数据
    String result = jedisClient.hget("qqqq:", 11 + "");
    if (!StringUtils.isBlank(result)) {
        //把字符串转换成list
        List<TaskManage> resultList = JSON.parseArray(result, TaskManage.class);  

    } catch (Exception e) {
        e.printStackTrac```
();
    }
}

直接存储List

上面的例子需要转化list,比较繁琐,那能不能直接存储list集合呢? 这就要需要序列化List集合了。
序列化公共类:(本例是Jdk自带jar序列化,存在效率低,后续引入第三方jar)

package com.cci.community.service.untils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/** 
 * 序列化工具类 
 * @author cym
 * 2017/5/24
 */
public class SerializeUtil {
        /**
         * 单个序列化
         * @param object
         * @return
         */
        public static byte[] serialize(Object object) {
            if (object == null) {
                return null;
            }
            ObjectOutputStream oos = null;
            ByteArrayOutputStream baos = null;
            byte[] bytes = null;
            try {
                // 序列化
                baos = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(baos);
                oos.writeObject(object);
                bytes = baos.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                close(oos);
                close(baos);
            }
            return bytes;
        }

        /**
         * 单个反序列化
         * 
         * @param bytes
         * @return
         */
        public static Object unserialize(byte[] bytes) {
            if (bytes == null) {
                return null;
            }
            ByteArrayInputStream bais = null;
            ObjectInputStream ois = null;
            try {
                // 反序列化
                bais = new ByteArrayInputStream(bytes);
                ois = new ObjectInputStream(bais);
                return ois.readObject();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                close(bais);
                close(ois);
            }
            return null;
        }

        /**
         * 序列化 list 集合
         * 
         * @param list
         * @return
         */
        public static byte[] serializeList(List<?> list) {
            if (list == null || list.size() == 0) {
                return null;
            }
            ObjectOutputStream oos = null;
            ByteArrayOutputStream baos = null;
            byte[] bytes = null;
            try {
                baos = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(baos);
                for (Object obj : list) {
                    oos.writeObject(obj);
                }
                bytes = baos.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                close(oos);
                close(baos);
            }
            return bytes;
        }

        /**
         * 反序列化 list 集合
         * 
         * @param lb
         * @return
         */
        public static List<?> unserializeList(byte[] bytes) {
            if (bytes == null) {
                return null;
            }
            List<Object> list = new ArrayList<Object>();
            ByteArrayInputStream bais = null;
            ObjectInputStream ois = null;
            try {
                // 反序列化
                bais = new ByteArrayInputStream(bytes);
                ois = new ObjectInputStream(bais);
                while (bais.available() > 0) {
                    Object obj = (Object) ois.readObject();
                    if (obj == null) {
                        break;
                    }
                    list.add(obj);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                close(bais);
                close(ois);
            }
            return list;
        }

        /**
         * 关闭io流对象
         * 
         * @param closeable
         */
        public static void close(Closeable closeable) {
            if (closeable != null) {
                try {
                    closeable.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

redis封装类JedisClientSingle :


    /**
     * 设置List集合
     * @param key
     * @param list
     */
    public void setList(String key ,List<?> list){
        Jedis jedis = jedisPool.getResource();
        try {
            if(list == null || list.size() == 0){
                jedis.set(key.getBytes(), "".getBytes());
            }else{//如果list为空,则设置一个空
                jedis.set(key.getBytes(), SerializeUtil.serializeList(list));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
    }

    /**
     * 获取List集合
     * @param key
     * @return
     */
    public List<?> getList(String key){
        Jedis jedis = jedisPool.getResource();
        if(jedis == null || !jedis.exists(key)){
            return null;
        }
        byte[] data = jedis.get(key.getBytes());        
        jedis.close();
        return SerializeUtil.unserializeList(data);
    }

测试:

@Autowired
private JedisClientSingle jedisClient;

//存储缓存
jedisClient.setList("getlist", list);
//取缓存
ist<TaskManage> list111 = (List<TaskManage>) jedisClient.getList("getlist");
<think>好的,我需要帮助用户解决使用Java的RedisTemplate将具有三层结构的List<Map>数据插入Redis的问题。首先,我得明确用户的具体需求。用户提到“保持数据的3层结构”,可能是指数据具有三层嵌套结构,例如List包含Map,而Map中的值又是另一个List或Map。这种情况下,如何正确序列化和存储是关键。 首先,回顾用户之前的提问和我的回答。之前我提供了如何配置RedisTemplate使用JSON序列化器来存储List<Map>,但用户现在需要处理更复杂的三层结构。之前的配置可能不足以处理嵌套结构,尤其是在反序列化时可能丢失类型信息,导致类型转换错误。 接下来,我需要考虑如何确保三层结构的数据在序列化和反序列化过程中保持完整。使用Jackson2JsonRedisSerializer时,默认配置可能无法正确保留泛型类型信息,尤其是在嵌套集合的情况下。因此,可能需要配置Jackson的ObjectMapper来启用类型信息存储,例如启用activateDefaultTyping,这样序列化的JSON会包含类型信息,反序列化时能正确还原对象结构。 另外,用户可能没有意识到Redis的数据结构选择对维护层级结构的重要性。例如,使用Redis的Hash结构来存储每个Map,或者使用List结构来保整个三层结构。需要根据用户的访问模式来建议合适的数据结构,但用户明确提到要保持三层结构,因此可能需要将整个对象作为值存储,而不是拆分成多个键。 还需要注意类型擦除的问题,尤其是在反序列化泛型集合时,必须使用TypeReference来明确指定类型,否则Jackson无法正确解析嵌套的泛型结构。例如,当从Redis读取数据时,Object的类型可能是List,但内部元素可能被反序列化为LinkedHashMap而不是用户期望的具体类型,因此需要显式转换。 此外,可能需要提醒用户关于Redis的过期时间和内管理,尤其是在存储大型嵌套结构时,避免内泄漏。同时,异常处理也很重要,比如处理序列化或反序列化失败的情况,确保程序健壮性。 最后,根据用户的需求生成相关问题,例如如何优化嵌套结构的序列化性能,如何处理更深层次的嵌套结构,或者如何选择合适的数据结构来维护层级关系。这些问题可以帮助用户进一步理解和优化他们的实现。</think>以下是实现三层嵌套数据结构存储的完整解决方案(基于$RedisTemplate$),包含结构设计、序列化配置和类型安全取方案: ### 一、数据结构定义示例 假设需要存储的三层结构为: $$ List<Map<String, List<Map<String, Object>>>> $$ 即: 1. 外层:List(一级结构) 2. 中层:Map的Value包含List(二级结构) 3. 内层:List内包含Map(三级结构) ### 二、关键配置增强(解决嵌套序列化问题) ```java @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> serializer = new EnhancedJacksonSerializer(); // 必须配置的类型提示策略 ObjectMapper mapper = new ObjectMapper(); mapper.activateDefaultTyping( mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY ); serializer.setObjectMapper(mapper); template.setDefaultSerializer(serializer); template.afterPropertiesSet(); return template; } // 自定义序列化器处理复杂类型 static class EnhancedJacksonSerializer extends Jackson2JsonRedisSerializer<Object> { public EnhancedJacksonSerializer() { super(Object.class); } } } ``` ### 三、数据取实现 #### 存储实现(保持完整结构) ```java public void save3LayerStructure(String redisKey, List<Map<String, List<Map<String, Object>>>> dataStructure) { // 直接存储完整结构 redisTemplate.opsForValue().set(redisKey, dataStructure); // 推荐设置TTL防止数据滞留 redisTemplate.expire(redisKey, 2, TimeUnit.HOURS); } ``` #### 读取实现(带类型安全校验) ```java public List<Map<String, List<Map<String, Object>>>> get3LayerStructure(String redisKey) { Object data = redisTemplate.opsForValue().get(redisKey); if (data == null) return Collections.emptyList(); try { ObjectMapper mapper = new ObjectMapper(); return mapper.convertValue(data, new TypeReference<List<Map<String, List<Map<String, Object>>>>>() {}); } catch (IllegalArgumentException e) { // 类型不匹配时处理 log.error("Type mismatch in deserialization", e); return handleDataRecovery(data); // 自定义恢复逻辑 } } ``` ### 四、结构验证方案 ```java // 验证数据结构完整性 public boolean validateStructure(Object data) { if (data instanceof List) { for (Object item : (List<?>) data) { if (!(item instanceof Map)) return false; for (Object value : ((Map<?,?>) item).values()) { if (!(value instanceof List)) return false; for (Object innerItem : (List<?>) value) { if (!(innerItem instanceof Map)) return false; } } } return true; } return false; } ``` ### 五、性能优化建议 1. **压缩存储**:启用Jackson的压缩功能 ```java mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true); mapper.enable(JsonWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN.getMask()); ``` 2. **选择性持久化**:对第三层Map使用Hash结构 ```java dataStructure.forEach(outerMap -> { outerMap.forEach((key, innerList) -> { String hashKey = "layer3_" + UUID.randomUUID(); redisTemplate.opsForHash().putAll(hashKey, innerList.stream() .flatMap(map -> map.entrySet().stream()) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue )) ); }); }); ``` --相关问题-- 1. 如何处理Redis中深度嵌套结构的版本兼容性问题? 2. 使用JSON序列化时如何避免类型信息泄露? 3. 哪种Redis数据结构最适合存储多层树形结构? 4. 如何监控Redis中复杂数据结构的存储效率?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值