如何将没有实现Serializable的类存储到Redis中

你有没有遇到过这种情况,需要将实例对象存储到Redis中,但是类的字段包含第三方依赖中的类并且此类没有 implements Serializable。执行时就会报没有序列化的错:

Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.entity.UserEntity]
	at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:43)
	at org.springframework.core.serializer.Serializer.serializeToByteArray(Serializer.java:56)
	at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:60)
	... 54 more

有三种方法如下

ObjectHashMapper

ObjectHashMapper使用对象到哈希映射。

需存redis的类如下:

用户类

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserEntity {

    /**
     * 用户名
     */
    private String username;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 所属部门
     */
    private Department department;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 手机号码
     */
    private List<String> phoneNumber;

    /**
     * 爱好
     */
    private Map<String, Object> hobby;
}

部门类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Department {

    /**
     * 部门id
     */
    private String depId;

    /**
     * 部门名称
     */
    private String depName;

    /**
     * 父级部门
     */
    private String parentId;
}

注意:上面两个类都没有实现序列化

通过ObjectHashMapper的toHash和fromHash实现

List<String> phoneNumbers = new ArrayList<>();
phoneNumbers.add("13016541724");
phoneNumbers.add("18826173452");

Map<String, Object> hobbys = new HashMap<>();
hobbys.put("运行", "打羽毛球");
hobbys.put("电影", "看电影");

UserEntity userEntity = UserEntity.builder().username("张三").age(18).department(
        Department.builder().depId("djinefn213445nkqk").depName("研发中心").parentId("ncdjkn3j4njndcinw").build()
).createTime(new Date()).phoneNumber(phoneNumbers).hobby(hobbys)
        .build();

ObjectHashMapper objectHashMapper = new ObjectHashMapper();
Map<byte[], byte[]> map = objectHashMapper.toHash(userEntity);
redisTemplate.opsForHash().put("TEST_OBJECT", "a",  map);


Map<byte[], byte[]> map1 = (Map<byte[], byte[]>) redisTemplate.opsForHash().get("TEST_OBJECT", "a");
        UserEntity userEntityRes = (UserEntity) objectHashMapper.fromHash(map1);

Jackson2HashMapper

Jackson2HashMapper通过使用FasterXMLJackson为域对象提供Redis哈希映射。可以将顶级属性映射为哈希字段名,也可以选择展平结构。简单类型映射到简单值。复杂类型(嵌套对象、集合、映射等)表示为嵌套JSON。Jackson2HashMapper平展为所有嵌套属性创建单独的哈希条目,并尽可能将复杂类型解析为简单类型。

List<String> phoneNumbers = new ArrayList<>();
phoneNumbers.add("13016541724");
phoneNumbers.add("18826173452");

Map<String, Object> hobbys = new HashMap<>();
hobbys.put("运行", "打羽毛球");
hobbys.put("电影", "看电影");

UserEntity userEntity = UserEntity.builder().username("张三").age(18).department(
        Department.builder().depId("djinefn213445nkqk").depName("研发中心").parentId("ncdjkn3j4njndcinw").build()
).createTime(new Date()).phoneNumber(phoneNumbers).hobby(hobbys)
        .build();

Jackson2HashMapper jackson2HashMapper = new Jackson2HashMapper(true);
Map<String, Object> map = jackson2HashMapper.toHash(userEntity);
redisTemplate.opsForHash().put("TEST_OBJECT", "a",  map);


Map<String, Object> map1 = (Map<String, Object>) redisTemplate.opsForHash().get("TEST_OBJECT", "a");
UserEntity userEntityRes = (UserEntity) jackson2HashMapper.fromHash(map1);

BeanUtilsHashMapper

BeanUtilsHashMapper使用Spring的BeanUtils。

List<String> phoneNumbers = new ArrayList<>();
phoneNumbers.add("13016541724");
phoneNumbers.add("18826173452");

Map<String, Object> hobbys = new HashMap<>();
hobbys.put("运行", "打羽毛球");
hobbys.put("电影", "看电影");

UserEntity userEntity = UserEntity.builder().username("张三").age(18).department(
        Department.builder().depId("djinefn213445nkqk").depName("研发中心").parentId("ncdjkn3j4njndcinw").build()
).createTime(new Date()).phoneNumber(phoneNumbers).hobby(hobbys)
        .build();

BeanUtilsHashMapper beanUtilsHashMapper = new BeanUtilsHashMapper(UserEntity.class);
Map<String, Object> map = beanUtilsHashMapper.toHash(userEntity);
redisTemplate.opsForHash().put("TEST_OBJECT", "a",  map);


Map<String, Object> map1 = (Map<String, Object>) redisTemplate.opsForHash().get("TEST_OBJECT", "a");
UserEntity userEntityRes = (UserEntity) beanUtilsHashMapper.fromHash(map1);

运行会报

Caused by: org.apache.commons.beanutils.ConversionException: Can't convert value '' to type interface java.util.List
	at org.apache.commons.beanutils.converters.AbstractConverter.conversionException(AbstractConverter.java:474)
	at org.apache.commons.beanutils.converters.StringConverter.convertToType(StringConverter.java:96)
	at org.apache.commons.beanutils.converters.AbstractConverter.handleMissing(AbstractConverter.java:312)
	... 58 more

从报错中可以看出,Listinterface,无法转换。将List改为ArrayList,Map改为HashMap再执行报如下错误

Caused by: org.apache.commons.beanutils.ConversionException: Can't convert value '' to type class java.util.ArrayList
	at org.apache.commons.beanutils.converters.AbstractConverter.conversionException(AbstractConverter.java:474)
	at org.apache.commons.beanutils.converters.StringConverter.convertToType(StringConverter.java:96)
	at org.apache.commons.beanutils.converters.AbstractConverter.handleMissing(AbstractConverter.java:312)
	... 58 more

排查会发现,phoneNumber的两个手机号码toHash后变成了一个手机号码

7bcf4864b1ffb8654b525ac6bbcbf5ed.png

这是BeanUtilsHashMapper的一个缺陷。

### 回答1: 如果您想将Netty Channel对象存储Redis,您需要将其序列化为字节数组,然后将其存储Redis。但是,由于Channel实现没有实现java.io.Serializable接口,因此无法直接进行序列化。 一种解决方案是,您可以使用第三方序列化库(例如Google的Protobuf或Apache的Avro)来序列化Channel对象。然后您可以使用Redis的字节数组型(例如Redis的SET命令)将序列化后的对象存储Redis。 另一种解决方案是,您可以使用Netty提供的编码器和解码器(Encoder和Decoder)来实现网络传输和存储Channel对象。具体来说,您可以定义一个编码器,将Channel对象编码为字节数组,然后使用Redis的字节数组型将其存储Redis。然后,您可以定义一个解码器,将字节数组解码为Channel对象。 希望这些信息能帮助您。 ### 回答2: Netty的Channel实现不是Serializable,这意味着无法直接将Channel对象存储Redis。然而,我们仍然可以将Channel相关的信息存储Redis,以便在需要时能够恢复Channel对象的状态。 一种常见的办法是将Channel的唯一标识符(如id或者name)存储Redis,以便在需要时能够重新获取到对应的Channel对象。具体的操作流程如下: 1. 在Channel被创建时,为每个Channel生成一个唯一的标识符,并与Channel对象建立关联。可以使用Channel的id或者name作为唯一标识符。 2. 在Channel关闭时,将Channel的唯一标识符从Redis删除,以释放资源。 3. 需要存储Channel对象时,可以将Channel对象的相关信息(例如IP地址、端口号等)存储Redis的Hash数据结构,并以Channel的唯一标识符作为Hash的Key。 4. 当需要获取Channel对象时,从Redis获取Channel的唯一标识符,并使用该标识符与Channel对象建立新的关联。 需要注意的是,由于Channel对象无法被序列化存储,所以无法将完整的Channel对象存储Redis。相反,我们只能存储Channel对象的相关信息,并根据需要重新建立与Channel对象的关联。这种方式牺牲了一些性能,但能够满足将Channel对象间接存储Redis的需求。 ### 回答3: 要将Netty的Channel对象存储Redis,可以使用Redis存储Channel的ID而不是直接存储Channel对象本身。由于Channel对象在Netty不是可序列化的,无法直接将其存储Redis。 1. 首先,在Netty的ChannelHandler,为每个Channel生成唯一的ID。可以使用Channel的远程地址或者自定义的ID生成算法来生成ID。 2. 将生成的ID与Channel对象建立映射关系,可以使用Map或者ConcurrentMap来存储Channel和对应ID的映射关系。这样可以通过ID来获取对应的Channel对象。 3. 将生成的ID存储Redis,可以使用Redis的哈希型(Hash)来存储Channel的ID和对应的状态信息。将Channel的ID作为Hash的key,状态信息作为Hash的value。 4. 当需要从Redis获取Channel对象时,通过获取ID,再根据ID从映射关系取出对应的Channel对象。 5. 当需要向Redis保存Channel对象时,先生成唯一的ID,然后将ID存储Redis,并将相应的Channel对象与ID建立映射关系。 这样,就可以实现将Netty的Channel对象存储Redis。通过存储Channel的ID,而不是直接存储Channel对象本身,解决了Channel不可序列化的问题,并且能够在需要的时候方便地从Redis获取Channel对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java开发高级进阶公众号

苦不堪言

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值