通过Spring Data存储库将POJO与大地图保存在一起会导致StackOverflowError

问题描述

通常:我正在从Kafka Stream读取序列化对象(作为JSON),并尝试使用Spring Data存储库将其保存到Redis。
经过两次调用(对象尚未保存到Redis)到repository.save(),我得到了StackOverFlowError:

Exception in thread "processOffers-applicationId-1c24ef63-baae-47b9-beb7-5e6517736bc4-StreamThread-1" java.lang.StackOverflowError
at org.springframework.data.util.Lazy.get(Lazy.java:94)
at org.springframework.data.mapping.model.AnnotationBasedPersistentProperty.usePropertyAccess(AnnotationBasedPersistentProperty.java:277)
at org.springframework.data.mapping.model.BeanWrapper.getProperty(BeanWrapper.java:134)
at org.springframework.data.mapping.model.BeanWrapper.getProperty(BeanWrapper.java:115)
at org.springframework.data.redis.core.convert.MappingRedisConverter.lambda$writeInternal$2(MappingRedisConverter.java:601)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:353)
at org.springframework.data.redis.core.convert.MappingRedisConverter.writeInternal(MappingRedisConverter.java:597)
at org.springframework.data.redis.core.convert.MappingRedisConverter.lambda$writeInternal$2(MappingRedisConverter.java:639)

序列化的POJO看起来像这样:

@Data
@With
@NoArgsConstructor
@AllArgsConstructor
@RedisHash("students")
public class Student {
    @Id
    @JsonProperty("student_id")
    private long id;

    @JsonProperty("entities")
    private Map<String,Object> entities = new HashMap<>();
}

地图实体包含100多个条目,带有嵌套的地图(对象)。
有趣的部分:如果我将地图清空,则一切正常,数据立即保存到Redis。

POJO的相应存储库:

@Repository
public interface StudentRepository extends CrudRepository<Student,Long> {
}

此外,我为Long id字段定义了RedisCustomConversion:

@Component
@ReadingConverter
public class BytesToLongConverter implements Converter<byte[],Long> {
    @Override
    public Long convert(final byte[] source) {
        ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
        buffer.put(source);
        buffer.flip();
        return buffer.getLong();
    }
}

@Component
@WritingConverter
public class LongToBytesConverter implements Converter<Long,byte[]> {
    @Override
    public byte[] convert(final Long source) {
        ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
        buffer.putLong(source);
        return buffer.array();
    }
}

Redis配置类如下:

@Configuration
@EnableRedisRepositories
public class RedisConfiguration {
    @Bean
    @Primary
    public RedisProperties redisProperties() {
        return new RedisProperties();
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        var config = new RedisStandaloneConfiguration();
        var props = redisProperties();
        config.setHostName(props.getHost());
        config.setPort(props.getPort());
        return new JedisConnectionFactory(config);
    }

    @Bean
    public RedisTemplate<String,Object> redisTemplate() {
        var template = new RedisTemplate<String,Object>();
        template.setConnectionFactory(redisConnectionFactory());
        template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }

    @Bean
    public RedisCustomConversions redisCustomConversions(LongToBytesConverter longToBytes,BytesToLongConverter bytesToLong) {
        return new RedisCustomConversions(Arrays.asList(longToBytes,bytesToLong));
    }
}

UPD : 我在Spring Data Redis Jira上找到了这个issue,但分辨率设置为“固定”,所以对我来说似乎很奇怪。

解决方法

我已经使用GenericJackson2JsonRedisSerializer在POJO中为我的内部地图定义了自定义WritingConverter和ReadingConverter,一切都解决了! 代码:

@Component
@WritingConverter
public class FieldsToBytesConverter implements Converter<Map<String,Object>,byte[]> {
    private final RedisSerializer serializer;

    public FieldsToBytesConverter() {
        serializer = new GenericJackson2JsonRedisSerializer();
    }

    @Override
    public byte[] convert(Map<String,Object> value) {
        return serializer.serialize(value);
    }
}

@Component
@ReadingConverter
public class BytesToFieldsConverter implements Converter<byte[],Map<String,Object>> {
    private final GenericJackson2JsonRedisSerializer serializer;

    public BytesToFieldsConverter() {
        serializer = new GenericJackson2JsonRedisSerializer();
    }

    @Override
    public Map<String,Object> convert(byte[] value) {
        return (Map<String,Object>) serializer.deserialize(value);
    }
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...