【发布时间】:2018-09-06 10:15:06
【问题描述】:
我希望有人可以提供帮助,因为我真的不知道如何解决以下问题:
我想用 Spring Data Redisin 分别序列化 Entity 和 ThumbnailUrlEntity 的一些对象(见下面的代码)@ 987654324@。由于这些类有我不想序列化的成员,因此我尝试以各种方式忽略它们,方法是使用 @JsonIgnoreProperties 注释类本身或使用 @JsonIgnore 或 Filters 注释相应的成员/属性RedisConfig.javaCustomConversion。
现在的问题是,无论我尝试哪种忽略方式,当我尝试保存相应的对象时,我总是得到相同的错误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
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 中配置它(再次参见下面的代码),但这仍然没有帮助,我得到完全相同的异常..
顺便说一句,我已经在此处考虑了“Christoph Strobl”的答案:spring-data-redis Jackson serialization :-)
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);
}
}
}
【问题讨论】:
-
使用@jsonignore。您的错误与序列化无关。
-
如前所述,我之前尝试过,但没有帮助。而且我很确定问题出在 Redis 序列化的某个地方,因为我尝试在没有 Redis 的情况下进行序列化(只需使用 Jackson 的字符串)并且忽略属性或自定义 ObjectMapper 的注释完美地工作..只有当我尝试这样做时使用 Spring Data Redis,我遇到了这些问题。
-
也试试
@transient注解 -
是的,我也尝试过@Transient :) 问题在于,它不会忽略该属性,而是在序列化时将其设置为 null。我想完全摆脱财产
-
他们的文档说对象的基本接口序列化和反序列化为字节数组(二进制数据)。建议将实现设计为在序列化和反序列化方面处理空对象/空数组。请注意,Redis 不接受 null 键或值,但可以返回 null 回复(对于不存在的键)。