spring-projects / spring-projects/spring-data-rest

Polymorphic projections for associations [DATAREST-783]

Open
#1,156 1 comment 0 reactions 1 assignee View on GitHub

@odrotbohm is already working on this.

Since Dec 31, 2020.

type: bug
Dominant language
Java
Stars
958
Forks
568
PR merge metrics
No merged PRs in 30d

Description

Mikhail Kadan opened DATAREST-783 and commented

Consider the following case:

A Book has multiple authors, each of them may be either a Professional or an Amateur, e.g.:

class Book { String title; @OneToMany Set<Author> authors; }

class Person { String name; int age; }

class Author extends Person { @ManyToOne Book book; String licenseId; }

class AmateurAuthor extends Author { String favoriteColor; }

class ProfessionalAuthor extends Author { int professionalExperience; }

Corresponding repositories:

interface BookRepository {}

interface AmateurAuthorRepository {}

interface ProfessionalAuthorRepository {}

And the following projections:

@Projection(name = "default", types = Book.class)
public interface BookProjection { String getTitle(); Set<AuthorProjection> getAuthors(); }

public interface PersonProjection { String getName(); }

public interface AuthorProjection extends PersonProjection { String getLicenseId(); }

@Projection(name = "default", types = AmateurAuthor.class)
interface AmateurAuthorProjection extends AuthorProjection { String getFavoriteColor(); }

@Projection(name = "default", types = ProfessionalAuthor.class)
public interface ProfessionalAuthorProjection extends AuthorProjection { int getProfessionalExperience(); }

What do we have there is a Book having a set of Authors, each of them may be either an AmateurAuthor or a Professional Author.

What do I expect to get when I ask for a book resource/collection with a "default" projection:

... {
  "title": "Book #1",
  "authors": {
    "_embedded": {
      "professionals": [{"name": "Name #1", "licenseId": "License #1", professionalExperience": 10, "_links": {...}}, ...]
      "amateurs": [{"name": "Name #2", "licenseId": "License #2", "favoriteColor": "pink", "_links": {...}}, ...]
    }
  }
} ...

But that's what I actually get:

... {
  "title": "Book #1",
  "authors": [
    {"name": "Name #1", "licenseId": "License #1", "_links": {...}}, ...
    {"name": "Name #2", "licenseId": "License #2", "_links": {...}}, ...
  }]
} ...

As you see, I have no info on the type of an author and I don't get any additional properties defined for their polymorphic projections. That means only the base projection (AuthorProjection), and not inherited projections (AmateurAuthorProjection, ProfessionalAuthorProjection) is considered when constructing the response.

That could be future proved by defining:

@Projection(name = "default2", types = Book.class)
public interface BookProjection2 { String getTitle(); Set<PersonProjection> getAuthors(); }

And then requesting the "default2" provides:

... {
  "title": "Book #1",
  "authors": [
    {"name": "Name #1", "_links": {...}}, ...
    {"name": "Name #2", "_links": {...}}, ...
  }]
} ...

Another approach I tried was:

@Projection(name = "default3", types = Book.class)
public interface BookProjection3 { String getTitle(); Set<Author> getAuthors(); }

But that way I get no projection for "authors" at all:

... {
  "title": "Book #1",
  "authors": [
    {"name": "Name #1", "age": 10, "licenseId": "License #1", "professionalExperience": 10}, ...
    {"name": "Name #2", "age": 10, "licenseId": "License #1", "favoriteColor": "pink"}, ...
  }]
} ...

As for me this relates somehow to the https://jira.spring.io/browse/DATAREST-464
I have also included a sample project to demonstrate this issue


Affects: 2.4.2 (Gosling SR2)

Attachments:

1 votes, 1 watchers

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.