我有两个实体。父母是:
public class One implements Serializable {
@Id
private Long id;
@OneToMany(mappedBy="one", cascade = CascadeType.ALL)
@RestResource(exported = false)
private List<Two> twos;
}
孩子是:
@Entity
@IdClass(TwoPk.class)
public class Two implements Serializable {
@Id
@ManyToOne
@JoinColumn(name="ONE")
private One one;
}
@Id
@ManyToOne
@JoinColumn(name="OTHER_OBJ")
private OtherObj otherObj;
}
关键类TwoPk是:
@Embeddable
public class TwoPK implements Serializable {
@Column(name="ONE")
private Long one;
@Column(name="OTHER_OBJ")
private Long otherObj;
}
组成密钥的第三个对象具有以下结构:
@Entity
public class OtherObj implements Serializable {
@Id
private Long id;
}
我有两个实体的存储库。
感谢@RestResource(exported = false)我可以在一次调用中发布一个包含所有两个孩子的One实体。 但是我想通过不使用“exported = false”来完成同样的事情,因为我想看到链接。此外,我还想导出两个存储库。 有可能吗?
最好还有一个反序列化程序可以使用URI和完整的JSON对象作为“twos”数组的子代。
答案 0 :(得分:0)
我结束了自己的解决方案。我们的想法是创建一个DeserializerModifier,它创建一个特殊的序列化器:
最后,您需要使用@UriOrBean注释在更新期间接受URI或复杂对象的字段(POST,PUT或PATCH)。
无需使用@RestResource(exported = false)
首先,我创建一个注释,以注释desider属性:
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UriOrBean {
}
然后我创建一个特殊的两遍反序列化器,第一个传递创建一个上下文反序列化器:
public class UriOrBeanDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
private PersistentProperty<?> property;
private ConversionService conversionService;
public UriOrBeanDeserializer(PersistentProperty<?> property, ConversionService conversionService) {
this.property = property;
this.conversionService = conversionService;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
JavaType propertyType = property.getType();
JavaType contentType = propertyType;
if (propertyType.isCollectionLikeType()) {
contentType = propertyType.getContentType();
}
JsonDeserializer<Object> delegatee = ctxt.findNonContextualValueDeserializer(contentType);
UriOrBeanDeserializerInternal objectDeserializer = new UriOrBeanDeserializerInternal(delegatee, new UriStringDeserializer(this.property, conversionService));
JsonDeserializer<?> result;
if (propertyType.isCollectionLikeType()) {
CollectionLikeType collectionType = ctxt.getTypeFactory().constructCollectionLikeType(propertyType.getRawClass(),
contentType);
ValueInstantiator instantiator = new CollectionValueInstantiator(this.property);
result = new CollectionDeserializer(collectionType, objectDeserializer, null, instantiator);
} else {
result = objectDeserializer;
}
return result;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return null;
}
}
第二遍是真正的反序列化器,有两个代理,用于URI和实际对象。
private static class UriOrBeanDeserializerInternal extends DelegatingDeserializer {
private static final long serialVersionUID = 2633089079583425906L;
private JsonDeserializer<?> uriStringDeserializer;
public UriOrBeanDeserializerInternal(JsonDeserializer<?> delegatee, JsonDeserializer<?> uriStringDeserializer) {
super(delegatee);
this.uriStringDeserializer = uriStringDeserializer;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
return this;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonToken token = p.currentToken();
if (token.isScalarValue()) {
return uriStringDeserializer.deserialize(p, ctxt);
}
return _delegatee.deserialize(p, ctxt);
}
@Override
protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
return new UriOrBeanDeserializerInternal(_delegatee, uriStringDeserializer);
}
}
从Spring Data Rest(C)Springframework复制了几个类:
/**
* Classe quasi completamente copiata dall'equivalente in Spring Data Rest.
* {@link ValueInstantiator} to create collection or map instances based on the type of the configured
* {@link PersistentProperty}.
*
* @author Oliver Gierke
*/
private static class CollectionValueInstantiator extends ValueInstantiator {
private final PersistentProperty<?> property;
/**
* Creates a new {@link CollectionValueInstantiator} for the given {@link PersistentProperty}.
*
* @param property must not be {@literal null} and must be a collection.
*/
public CollectionValueInstantiator(PersistentProperty<?> property) {
Assert.notNull(property, "Property must not be null!");
Assert.isTrue(property.isCollectionLike() || property.isMap(), "Property must be a collection or map property!");
this.property = property;
}
/*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.deser.ValueInstantiator#getValueTypeDesc()
*/
@Override
public String getValueTypeDesc() {
return property.getType().getName();
}
/*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.deser.ValueInstantiator#createUsingDefault(com.fasterxml.jackson.databind.DeserializationContext)
*/
@Override
public Object createUsingDefault(DeserializationContext ctxt) throws IOException, JsonProcessingException {
Class<?> collectionOrMapType = property.getType();
return property.isMap() ? CollectionFactory.createMap(collectionOrMapType, 0)
: CollectionFactory.createCollection(collectionOrMapType, 0);
}
}
/**
* Classe quasi completamente copiata dall'equivalente in Spring Data Rest.
*
* Custom {@link JsonDeserializer} to interpret {@link String} values as URIs and resolve them using a
* {@link UriToEntityConverter}.
*
* @author Oliver Gierke
* @author Valentin Rentschler
*/
private static class UriStringDeserializer extends StdDeserializer<Object> {
private static final long serialVersionUID = -2175900204153350125L;
private static final String UNEXPECTED_VALUE = "Expected URI cause property %s points to the managed domain type!";
private static final TypeDescriptor URI_DESCRIPTOR = TypeDescriptor.valueOf(URI.class);
private final PersistentProperty<?> property;
private final ConversionService conversionService;
/**
* Creates a new {@link UriStringDeserializer} for the given {@link PersistentProperty} using the given
* {@link UriToEntityConverter}.
*
* @param property must not be {@literal null}.
* @param converter must not be {@literal null}.
*/
public UriStringDeserializer(PersistentProperty<?> property, ConversionService conversionService) {
super(property.getActualType());
this.property = property;
this.conversionService = conversionService;
}
/*
* (non-Javadoc)
* @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
*/
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String source = jp.getValueAsString();
if (!StringUtils.hasText(source)) {
return null;
}
try {
URI uri = new UriTemplate(source).expand();
TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(property.getActualType());
return conversionService.convert(uri, URI_DESCRIPTOR, typeDescriptor);
} catch (IllegalArgumentException o_O) {
throw ctxt.weirdStringException(source, URI.class, String.format(UNEXPECTED_VALUE, property));
}
}
/**
* Deserialize by ignoring the {@link TypeDeserializer}, as URIs will either resolve to {@literal null} or a
* concrete instance anyway.
*
* @see com.fasterxml.jackson.databind.deser.std.StdDeserializer#deserializeWithType(com.fasterxml.jackson.core.JsonParser,
* com.fasterxml.jackson.databind.DeserializationContext,
* com.fasterxml.jackson.databind.jsontype.TypeDeserializer)
*/
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
throws IOException {
return deserialize(jp, ctxt);
}
}
DeserializerModifier:
@Component
public class UriOrBeanDeserializerModifier extends BeanDeserializerModifier implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
BeanDeserializerBuilder builder) {
PersistentEntities entities = applicationContext.getBean(PersistentEntities.class);
Associations associationLinks = applicationContext.getBean(Associations.class);
ConversionService conversionService = applicationContext.getBean("defaultConversionService", ConversionService.class);
Iterator<SettableBeanProperty> properties = builder.getProperties();
PersistentEntity<?, ?> entity = entities.getPersistentEntity(beanDesc.getBeanClass());
if (entity == null) {
return builder;
}
while (properties.hasNext()) {
SettableBeanProperty property = properties.next();
PersistentProperty<?> persistentProperty = entity.getPersistentProperty(property.getName());
if (associationLinks.isLookupType(persistentProperty) || !associationLinks.isLinkableAssociation(persistentProperty)) {
continue;
}
if (property.getAnnotation(UriOrBean.class) != null) {
UriOrBeanDeserializer deserializer = new UriOrBeanDeserializer(persistentProperty, conversionService);
builder.addOrReplaceProperty(property.withValueDeserializer((JsonDeserializer<?>) deserializer), false);
}
}
return builder;
}
}
最后是RepositoryRestConfigurerAdapter中的配置:
@Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.registerModule(new Module() {
@Override
public String getModuleName() {
return "it.eng.intesasanpaolo.reng0.be";
}
@Override
public Version version() {
return Version.unknownVersion();
}
@Override
public void setupModule(SetupContext context) {
context.addBeanDeserializerModifier(uriOrBeanDeserializerModifier);
}
});
}