Spring Data Rest Repository с абстрактным классом / наследованием
Я не могу получить Spring Data Rest с работающим наследованием классов.
Я хотел бы иметь одну конечную точку JSON, которая обрабатывает все мои конкретные классы.
Repo:
public interface AbstractFooRepo extends KeyValueRepository<AbstractFoo, String> {}
Абстрактный класс:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = MyFoo.class, name = "MY_FOO")
})
public abstract class AbstractFoo {
@Id public String id;
public String type;
}
Конкретный класс:
public class MyFoo extends AbstractFoo { }
Теперь при звонке POST /abstractFoos
с {"type":"MY_FOO"}
, он говорит мне: java.lang.IllegalArgumentException: PersistentEntity must not be null!
,
Это, кажется, происходит, потому что Spring не знает о MyFoo
,
Есть ли способ рассказать Spring Data REST о MyFoo
без создания репозитория и конечной точки REST для него?
(Я использую Spring Boot 1.5.1 и Spring Data REST 2.6.0)
РЕДАКТИРОВАТЬ:
Application.java:
@SpringBootApplication
@EnableMapRepositories
public class Application {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2 ответа
Я использую Spring Boot 1.5.1
и Spring Data Release Ingalls
,
KeyValueRepository
не работает с наследованием. Он использует имя класса каждого сохраненного объекта, чтобы найти соответствующее хранилище ключей-значений. Например save(new Foo())
поместит сохраненный объект в Foo
коллекция. А также abstractFoosRepo.findAll()
будет смотреть в AbstractFoo
коллекция и не найдет Foo
объект.
Вот рабочий код с использованием MongoRepository:
Application.java
По умолчанию Spring Boot Application Starter.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
AbstractFoo.java
Я проверял
include = JsonTypeInfo.As.EXISTING_PROPERTY
а такжеinclude = JsonTypeInfo.As.PROPERTY
, Оба, кажется, работают нормально!Можно даже зарегистрировать подтипы Jackson с помощью пользовательского модуля JacksonModule.
ВАЖНЫЙ:
@RestResource(path="abstractFoos")
настоятельно рекомендуется. Остальное_links.self
ссылки будут указывать на/foos
а также/bars
вместо/abstractFoos
,
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Foo.class, name = "MY_FOO"),
@JsonSubTypes.Type(value = Bar.class, name = "MY_Bar")
})
@Document(collection="foo_collection")
@RestResource(path="abstractFoos")
public abstract class AbstractFoo {
@Id public String id;
public abstract String getType();
}
AbstractFooRepo.java
Ничего особенного здесь
public interface AbstractFooRepo extends MongoRepository<AbstractFoo, String> { }
Foo.java & Bar.java
@Persistent
public class Foo extends AbstractFoo {
@Override
public String getType() {
return "MY_FOO";
}
}
@Persistent
public class Bar extends AbstractFoo {
@Override
public String getType() {
return "MY_BAR";
}
}
FooRelProvider.java
- Без этой части выходные данные объектов будут разделены на два массива под
_embedded.foos
а также_embedded.bars
, supports
метод гарантирует, что для всех классов, которые расширяютсяAbstractFoo
, объекты будут размещены в_embedded.abstractFoos
,
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FooRelProvider extends EvoInflectorRelProvider {
@Override
public String getCollectionResourceRelFor(final Class<?> type) {
return super.getCollectionResourceRelFor(AbstractFoo.class);
}
@Override
public String getItemResourceRelFor(final Class<?> type) {
return super.getItemResourceRelFor(AbstractFoo.class);
}
@Override
public boolean supports(final Class<?> delimiter) {
return AbstractFoo.class.isAssignableFrom(delimiter);
}
}
РЕДАКТИРОВАТЬ
- добавленной
@Persistent
вFoo.java
а такжеBar.java
, (Добавление его вAbstractFoo.java
не работает). Без этой аннотации я получил NullPointerExceptions при попытке использовать аннотации проверки JSR 303 в унаследованных классах.
Пример кода для воспроизведения ошибки:
public class A {
@Id public String id;
@Valid public B b;
// @JsonTypeInfo + @JsonSubTypes
public static abstract class B {
@NotNull public String s;
}
// @Persistent <- Needed!
public static class B1 extends B { }
}
Пожалуйста, смотрите обсуждение в этой решенной задаче jira для деталей о том, что в настоящее время поддерживается в spring-data-rest относительно JsonTypeInfo
, И это задание Джира о том, чего еще не хватает.
Подводя итог - только @JsonTypeInfo
с include=JsonTypeInfo.As.EXISTING_PROPERTY
в настоящее время работает для сериализации и десериализации.
Кроме того, вам нужна пружина data-rest 2.5.3 (Hopper SR3) или новее, чтобы получить эту ограниченную поддержку.
Пожалуйста, посмотрите мой пример приложения - https://github.com/mduesterhoeft/spring-data-rest-entity-inheritance/tree/fixed-hopper-sr3-snapshot
С include=JsonTypeInfo.As.EXISTING_PROPERTY
информация о типе извлекается из обычного свойства. Пример помогает понять смысл этого способа добавления информации о типе:
Абстрактный класс:
@Entity @Inheritance(strategy= SINGLE_TABLE)
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,
include=JsonTypeInfo.As.EXISTING_PROPERTY,
property="type")
@JsonSubTypes({
@Type(name="DECIMAL", value=DecimalValue.class),
@Type(name="STRING", value=StringValue.class)})
public abstract class Value {
@Id @GeneratedValue(strategy = IDENTITY)
@Getter
private Long id;
public abstract String getType();
}
И подкласс:
@Entity @DiscriminatorValue("D")
@Getter @Setter
public class DecimalValue extends Value {
@Column(name = "DECIMAL_VALUE")
private BigDecimal value;
public String getType() {
return "DECIMAL";
}
}