Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson module to handle abstract aggregate root and its subclasses in Spring Data REST

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?

like image 800
Patrik Mihalčin Avatar asked Oct 18 '22 12:10

Patrik Mihalčin


1 Answers

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.

like image 100
Cepr0 Avatar answered Nov 08 '22 01:11

Cepr0