Как правильно игнорировать членов класса при сериализации с Redis Redis? - PullRequest
0 голосов
/ 06 сентября 2018

Я надеюсь, что кто-то может помочь, так как у меня действительно нет больше идей о том, как решить следующую проблему: Я хочу сериализовать некоторые объекты Entity и ThumbnailUrlEntity (см. Код ниже) с помощью Red Data Redisin соответственно CrudRepository. Поскольку у классов есть члены, которые я не хочу сериализовать, я попытался их игнорировать различными способами, либо пометив сами классы с помощью @JsonIgnoreProperties, либо пометив соответствующие члены / свойства с помощью @JsonIgnore или Filters в RedisConfig.java на CustomConversion.

Теперь проблема в том, что независимо от того, какой способ игнорирования я пробую, я всегда получаю одну и ту же ошибку org.springframework.data.keyvalue.core.UncategorizedKeyValueException: Path to property must not be null or empty.; nested exception is java.lang.IllegalArgumentException: Path to property must not be null or empty., когда пытаюсь сохранить соответствующий объект. (подробнее см. Stacktrace ниже)

Я даже пробовал это с MixIn для тех классов, но это не работает также ...

Итак, вопрос: как правильно игнорировать свойства при сериализации объекта определенного класса с помощью Spring-Data-Redis?

Entity.java

@EqualsAndHashCode(exclude = "domainObject")
@ToString(exclude = "domainObject")
@NoArgsConstructor
//@JsonIgnoreProperties("domainObject")
@JsonFilter("myEntityFilter")
public abstract class Entity<T extends DomainObject> {
    @TimeToLive
    protected final static long TIME_TO_LIVE = 60 * 60 * 24;

    @Id
    @Indexed
    private String id;

//    @Transient
//    @JsonIgnore
    private T domainObject;

    public Entity(@NotNull T domainObject) {
        this.domainObject = domainObject;
    }

    public abstract Entity<T> createFromDomainObject(@NotNull T domainObject);

    public abstract void updateFromDomainObject(T domainObject);

    public void updateFromDomainObject() {
        this.updateFromDomainObject(this.getDomainObject());
    }

    public String getId() {
        return id;
    }

//    @Transient
//    @JsonIgnore
    public T getDomainObject() {
        return domainObject;
    }

    public void setDomainObject(T domainObject) {
        this.domainObject = domainObject;
    }
}

ThumbnailUrlListEntity.java

@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false, exclude = "parentList")
@ToString(exclude = "parentList")
@RedisHash("thumbnail_url_entity")
//@JsonIgnoreProperties("parentList")
@JsonFilter("myThumbnailUrlEntityFilter")
public class ThumbnailUrlEntity extends Entity<ThumbnailUrl> {

    @Indexed
    private String url;

    private Integer priority;

//    @Transient
//    @JsonIgnore
    private ThumbnailUrlListEntity parentList;

    public ThumbnailUrlEntity(@NotNull ThumbnailUrl domainObject) {
        super(domainObject);
        this.updateFromDomainObject(domainObject);
    }

    @Override
    public Entity<ThumbnailUrl> createFromDomainObject(@NotNull ThumbnailUrl domainObject) {
        ThumbnailUrlEntity entity = new ThumbnailUrlEntity();
        entity.url = domainObject.getUrl();
        entity.priority = domainObject.getPriority();
        return entity;
    }

    @Override
    public void updateFromDomainObject(@NotNull ThumbnailUrl domainObject) {
        this.url = domainObject.getUrl();
        this.priority = domainObject.getPriority();
    }

    public String getUrl() {
        return url;
    }

    public Integer getPriority() {
        return priority;
    }

    public void setPriority(Integer priority) {
        this.priority = priority;
    }

//    @Transient
//    @JsonIgnore
    public ThumbnailUrlListEntity getParentList() {
        return parentList;
    }

    public void setParentList(ThumbnailUrlListEntity parentList) {
        this.parentList = parentList;
    }
}

RedisConfig.java

@Configuration
@EnableRedisRepositories(basePackages = "nlp.floschne.thumbnailAnnotator.db")
public class RedisConfig {

    @Bean
    RedisConnectionFactory connectionFactory() {
        return new LettuceConnectionFactory();
    }

    @Bean
    RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {

        RedisTemplate<String, byte[]> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
        mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);


        SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept("domainObject");
        FilterProvider filters = new SimpleFilterProvider()
                .addFilter("myEntityFilter", theFilter)
                .addFilter("myThumbnailUrlEntityFilter", theFilter);

        mapper.setFilters(filters);


        template.setValueSerializer(new GenericJackson2JsonRedisSerializer(mapper));

        return template;
    }

    @Bean
    public RedisCustomConversions redisCustomConversions() {
        return new RedisCustomConversions(Arrays.asList(
                new EntityToBytesConverter(),
                new BytesToEntityConverter(),
                new ThumbnailUrlEntityToBytesConverter(),
                new BytesToThumbnailUrlEntityConverter()));
    }

    @WritingConverter
    public class EntityToBytesConverter implements Converter<Entity<?>, byte[]> {

        private final GenericJackson2JsonRedisSerializer serializer;

        public EntityToBytesConverter() {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);

            SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept("domainObject");
            FilterProvider filters = new SimpleFilterProvider().addFilter("myEntityFilter", theFilter);

            mapper.setFilters(filters);

            serializer = new GenericJackson2JsonRedisSerializer(mapper);
        }

        @Override
        public byte[] convert(Entity value) {
            return serializer.serialize(value);
        }
    }

    @ReadingConverter
    public class BytesToEntityConverter implements Converter<byte[], Entity<?>> {

        private final GenericJackson2JsonRedisSerializer serializer;

        public BytesToEntityConverter() {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);


            SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept("domainObject");
            FilterProvider filters = new SimpleFilterProvider().addFilter("myEntityFilter", theFilter);

            mapper.setFilters(filters);

            serializer = new GenericJackson2JsonRedisSerializer(mapper);
        }

        @Override
        public Entity convert(byte[] value) {
            return (Entity) serializer.deserialize(value);
        }
    }

    @WritingConverter
    public class ThumbnailUrlEntityToBytesConverter implements Converter<ThumbnailUrlEntity, byte[]> {

        private final GenericJackson2JsonRedisSerializer serializer;

        public ThumbnailUrlEntityToBytesConverter() {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);

            SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept("parentList");
            FilterProvider filters = new SimpleFilterProvider().addFilter("myThumbnailUrlEntityFilter", theFilter);

            mapper.setFilters(filters);

            serializer = new GenericJackson2JsonRedisSerializer(mapper);
        }

        @Override
        public byte[] convert(ThumbnailUrlEntity value) {
            return serializer.serialize(value);
        }
    }

    @ReadingConverter
    public class BytesToThumbnailUrlEntityConverter implements Converter<byte[], ThumbnailUrlEntity> {

        private final GenericJackson2JsonRedisSerializer serializer;

        public BytesToThumbnailUrlEntityConverter() {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
            mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);


            SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept("parentList");
            FilterProvider filters = new SimpleFilterProvider().addFilter("myThumbnailUrlEntityFilter", theFilter);

            mapper.setFilters(filters);

            serializer = new GenericJackson2JsonRedisSerializer(mapper);
        }

        @Override
        public ThumbnailUrlEntity convert(byte[] value) {
            return (ThumbnailUrlEntity) serializer.deserialize(value);
        }
    }
}

ThumbnailUrlRepository

public interface ThumbnailUrlEntityRepository extends CrudRepository<ThumbnailUrlEntity, String> {
}

StackTrace w / Exception

org.springframework.data.keyvalue.core.UncategorizedKeyValueException: Path to property must not be null or empty.; nested exception is java.lang.IllegalArgumentException: Path to property must not be null or empty.

    at org.springframework.data.keyvalue.core.KeyValuePersistenceExceptionTranslator.translateExceptionIfPossible(KeyValuePersistenceExceptionTranslator.java:55)
    at org.springframework.data.keyvalue.core.KeyValueTemplate.resolveExceptionIfPossible(KeyValueTemplate.java:459)
    at org.springframework.data.keyvalue.core.KeyValueTemplate.execute(KeyValueTemplate.java:345)
    at org.springframework.data.keyvalue.core.KeyValueTemplate.insert(KeyValueTemplate.java:158)
    at org.springframework.data.redis.core.RedisKeyValueTemplate.insert(RedisKeyValueTemplate.java:125)
    at org.springframework.data.keyvalue.core.KeyValueTemplate.insert(KeyValueTemplate.java:140)
    at org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository.save(SimpleKeyValueRepository.java:101)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:377)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:629)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:593)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:578)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy43.save(Unknown Source)
    at nlp.floschne.thumbnailAnnotator.db.repository.ThumbnailUrlEntityRepositoryTests.whenSaving_thenAvailableOnRetrieval(ThumbnailUrlEntityRepositoryTests.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
    at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalArgumentException: Path to property must not be null or empty.
    at org.springframework.util.Assert.hasText(Assert.java:276)
    at org.springframework.data.redis.core.convert.Bucket.put(Bucket.java:72)
    at org.springframework.data.redis.core.convert.MappingRedisConverter.writeToBucket(MappingRedisConverter.java:750)
    at org.springframework.data.redis.core.convert.MappingRedisConverter.writeInternal(MappingRedisConverter.java:571)
    at org.springframework.data.redis.core.convert.MappingRedisConverter.write(MappingRedisConverter.java:396)
    at org.springframework.data.redis.core.convert.MappingRedisConverter.write(MappingRedisConverter.java:122)
    at org.springframework.data.redis.core.RedisKeyValueAdapter.put(RedisKeyValueAdapter.java:208)
    at org.springframework.data.keyvalue.core.KeyValueTemplate.lambda$insert$0(KeyValueTemplate.java:165)
    at org.springframework.data.keyvalue.core.KeyValueTemplate.execute(KeyValueTemplate.java:343)
    ... 58 more

Обновление Я попытался использовать собственный сериализатор (см. Код ниже) и настроил его в своем RedisConfig (снова см. Код ниже), но это все равно не помогает, и я получаю точно такое же исключение ..

Кстати, я уже учел ответ "Кристофа Штробля" здесь: spring-data-redis Сериализация Джексона во внимание: -)

EntityJsonSerializer.java

 public class EntityJsonSerializer implements RedisSerializer<Entity> {

        private final ObjectMapper mapper;

        public EntityJsonSerializer() {
            this.mapper = new ObjectMapper();
            this.mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
            this.mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            this.mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            this.mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
            this.mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);


            SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept("domainObject", "parentList");

            FilterProvider filters = new SimpleFilterProvider()
                    .addFilter("myEntityFilter", theFilter)
                    .addFilter("myThumbnailUrlEntityFilter", theFilter);

            this.mapper.setFilters(filters);
        }

        public EntityJsonSerializer(ObjectMapper mapper) {
            this.mapper = mapper;
        }

        @Override
        public byte[] serialize(Entity t) throws SerializationException {
            try {
                return mapper.writeValueAsBytes(t);
            } catch (JsonProcessingException e) {
                throw new SerializationException(e.getMessage(), e);
            }
        }

        @Override
        public Entity deserialize(byte[] bytes) throws SerializationException {

            if (bytes == null) {
                return null;
            }

            try {
                return mapper.readValue(bytes, Entity.class);
            } catch (Exception e) {
                throw new SerializationException(e.getMessage(), e);
            }
        }
    }

Обновлен RedisConfig.java

@Configuration
@EnableRedisRepositories(basePackages = "nlp.floschne.thumbnailAnnotator.db")
public class RedisConfig {

    @Bean
    RedisConnectionFactory connectionFactory() {
        return new LettuceConnectionFactory();
    }

    @Bean
    RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {

        RedisTemplate<String, byte[]> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());

        template.setValueSerializer(new EntityJsonSerializer());

        return template;
    }

    @Bean
    public RedisCustomConversions redisCustomConversions() {
        return new RedisCustomConversions(Arrays.asList(
                new EntityToBytesConverter(),
                new BytesToEntityConverter(),
                new ThumbnailUrlEntityToBytesConverter(),
                new BytesToThumbnailUrlEntityConverter()));
    }

    @WritingConverter
    public class EntityToBytesConverter implements Converter<Entity<?>, byte[]> {

        private final EntityJsonSerializer serializer;

        public EntityToBytesConverter() {
            serializer = new EntityJsonSerializer();
        }

        @Override
        public byte[] convert(Entity value) {
            return serializer.serialize(value);
        }
    }

    @ReadingConverter
    public class BytesToEntityConverter implements Converter<byte[], Entity<?>> {

        private final EntityJsonSerializer serializer;

        public BytesToEntityConverter() {
            serializer = new EntityJsonSerializer();
        }

        @Override
        public Entity convert(byte[] value) {
            return (Entity) serializer.deserialize(value);
        }
    }

    @WritingConverter
    public class ThumbnailUrlEntityToBytesConverter implements Converter<ThumbnailUrlEntity, byte[]> {

        private final EntityJsonSerializer serializer;

        public ThumbnailUrlEntityToBytesConverter() {
            serializer = new EntityJsonSerializer();
        }

        @Override
        public byte[] convert(ThumbnailUrlEntity value) {
            return serializer.serialize(value);
        }
    }

    @ReadingConverter
    public class BytesToThumbnailUrlEntityConverter implements Converter<byte[], ThumbnailUrlEntity> {

        private final EntityJsonSerializer serializer;

        public BytesToThumbnailUrlEntityConverter() {
            serializer = new EntityJsonSerializer();
        }

        @Override
        public ThumbnailUrlEntity convert(byte[] value) {
            return (ThumbnailUrlEntity) serializer.deserialize(value);
        }
    }
}

1 Ответ

0 голосов
/ 06 сентября 2018

Я думаю, что вы можете сделать одно из следующих действий:

Во-первых, вы можете попытаться заставить текущую версию работать, это не так просто, как кажется.

Правильно ли настроен сериализатор для Redis? На ссылку есть такой ответ:

Используйте StringRedisTemplate для замены RedisTemplate.

По умолчанию RedisTemplate использует сериализацию Java, StringRedisTemplate. использует StringRedisSerializer.

Это тоже может решить твою проблему ...

Второй вариант использования выделенных POJO для Redis. Вы можете осуществлять обмен значениями вручную или с помощью Java bean mappers, например MapStruct .

Третьим вариантом может быть использование настраиваемого сериализатора

...