Jackson module to handle abstract aggregate root and its subclasses in Spring Data REST
jackson, spring-data, spring-data-rest
Solution
There is no need to create a Jackson module. To use a single table for inherited entities we can use @RestResource annotation to mark them as the same resources:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "criteria")
public abstract class Criterion extends AbstractEntity {
}
@RestResource(rel = "criteria", path = "criteria")
@Entity
public class NameCriterion extends Criterion {
private String name;
}
@RestResource(rel = "criteria", path = "criteria")
@Entity
public class TitleCriterion extends Criterion {
private String title;
}
@RepositoryRestResource(path = "criteria", collectionResourceRel = "criteria", itemResourceRel = "criterion")
public interface CriterionRepository extends JpaRepository<Criterion, Long> {
}
So it becomes possible to obtain all the resources (NameCriterion and TitleCriterion) in one output:
GET http://localhost:8080/api/criteria
{
"_embedded": {
"criteria": [
{
"name": "name1",
"_links": {
"self": {
"href": "http://localhost:8080/api/criteria/1"
},
"nameCriterion": {
"href": "http://localhost:8080/api/criteria/1"
}
}
},
{
"title": "title1",
"_links": {
"self": {
"href": "http://localhost:8080/api/criteria/2"
},
"titleCriterion": {
"href": "http://localhost:8080/api/criteria/2"
}
}
}
]
}
}
GET http://localhost:8080/api/criteria/1
{
"name": "name1",
"_links": {
"self": {
"href": "http://localhost:8080/api/criteria/1"
},
"nameCriterion": {
"href": "http://localhost:8080/api/criteria/1"
}
}
}
GET http://localhost:8080/api/criteria/2
{
"title": "title1",
"_links": {
"self": {
"href": "http://localhost:8080/api/criteria/2"
},
"titleCriterion": {
"href": "http://localhost:8080/api/criteria/2"
}
}
}
Working example.
Problem
I have Spring Data REST based application with repository ``` public interface CriterionRepository extends JpaRepository<Criterion, Long> { } ``` whereas `Criterion` is base class: ``` @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public abstract class Criterion extends AbstractEntity {} ``` and `NameCriterion` is its subclass ``` @Entity public class NameCriterion extends Criterion { private final String name; } ``` Spring Data REST exports the repository as REST resource and one can access it at http://localhost:8080/api/criteria/ Exported resource looks as follows: ``` { "_embedded": { "nameCriteria": [{ "_links": { "self": { "href": "http://localhost:8080/api/nameCriterion/1" }, "nameCriterion": { "href": "http://localhost:8080/api/nameCriterion/1" } } } ] }, "_links": { "self": { "href": "http://localhost:8080/api/criteria" }, "profile": { "href": "http://localhost:8080/api/profile/criteria" } }, "page": { "size": 20, "totalElements": 1, "totalPages": 1, "number": 0 } } ``` When I try to follow self link, there is no mapping for http://localhost:8080/api/nameCriterion/1 I can follow http://localhost:8080/api/criteria/1 though and I get response without name field from `NameCriterion` ``` { "_links": { "self": { "href": "http://localhost:8080/api/nameCriterion/1" }, "nameCriterion": { "href": "http://localhost:8080/api/nameCriterion/1" } } } ``` My assumption is it is a problem with Jackson mapper defined in REST exporter which is not tweaked correctly to handle abstract class `Criterion` used in `JpaRepository` as aggregate root. What Jackson customization should I apply to make it working properly? In other words, what Jackson module should I create?