Projektionen und Auszüge in Spring Data REST

Projektionen und Auszüge in Spring Data REST

1. Überblick

In diesem Artikel werden die Konzepte von Spring Data REST für Projektionen und Auszüge erläutert.

Wir werden lernen, wie manuse projections to create custom views of our models and how to use excerpts as default views to resource collections macht.

2. Unsere Domain-Modelle

Zunächst definieren wir unsere Domänenmodelle:Book undAuthor.

Werfen wir einen Blick auf die EntitätsklasseBook:

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false)
    private String title;

    private String isbn;

    @ManyToMany(mappedBy = "books", fetch = FetchType.EAGER)
    private List authors;
}

Und das Modell vonAuthor:

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false)
    private String name;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
      name = "book_author",
      joinColumns = @JoinColumn(
        name = "book_id", referencedColumnName = "id"),
      inverseJoinColumns = @JoinColumn(
        name = "author_id", referencedColumnName = "id"))
    private List books;
}

Die beiden Einheiten haben auch eine Viele-zu-Viele-Beziehung.

Als Nächstes definieren wir Standard-Spring Data-REST-Repositorys für jedes der Modelle:

public interface BookRepository extends CrudRepository {}
public interface AuthorRepository extends CrudRepository {}

Jetzt können wir auf den Endpunkt vonBookzugreifen, um bestimmteBook’s-Details mit seiner ID beihttp://localhost:8080/books/{id}: abzurufen

{
  "title" : "Animal Farm",
  "isbn" : "978-1943138425",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/books/1"
    },
    "book" : {
      "href" : "http://localhost:8080/books/1"
    },
    "authors" : {
      "href" : "http://localhost:8080/books/1/authors"
    }
  }
}

Beachten Sie, dass die Details des Autors nicht Teil der Antwort sind, da dasAuthor-Modell über ein Repository verfügt. Wir können jedoch den Link zu ihnen finden -http://localhost:8080/books/1/authors.

3. Projektion erstellen

Manchmalwe’re only interested in a subset or a custom view of an entity’s attributes. Für solche Fälle können wir Projektionen verwenden.

Erstellen wir mithilfe von Spring Data REST-Projektionen eine benutzerdefinierte Ansicht für unsereBook.

Wir beginnen mit der Erstellung eines einfachenProjection namensCustomBook:

@Projection(
  name = "customBook",
  types = { Book.class })
public interface CustomBook {
    String getTitle();
}

Beachten Sie, dassour projection is defined as an interface with an @Projection annotation. Wir können das Attributname verwenden, um den Namen der Projektion anzupassen, sowie das Attributtypes, um die Objekte zu definieren, auf die es angewendet wird.

In unserem Beispiel enthält die Projektion vonCustomBooknur dietitleeines Buches.

Schauen wir uns noch einmal die Darstellung vonBookan, nachdem wir unsereProjection:erstellt haben

{
  "title" : "Animal Farm",
  "isbn" : "978-1943138425",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/books/1"
    },
    "book" : {
      "href" : "http://localhost:8080/books/1{?projection}",
      "templated" : true
    },
    "authors" : {
      "href" : "http://localhost:8080/books/1/authors"
    }
  }
}

Toll, wir können einen Link zu unserer Projektion sehen. Überprüfen wir die Ansicht, die wir beihttp://localhost:8080/books/1?projection=customBook erstellt haben:

{
  "title" : "Animal Farm",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/books/1"
    },
    "book" : {
      "href" : "http://localhost:8080/books/1{?projection}",
      "templated" : true
    },
    "authors" : {
      "href" : "http://localhost:8080/books/1/authors"
    }
  }
}

Hier können wir sehen, dass wir nur das Feldtitle erhalten, währendisbn in der benutzerdefinierten Ansicht nicht mehr vorhanden ist.

In der Regel können wir beihttp://localhost:8080/books/1?projection=\{projection name}. auf das Ergebnis einer Projektion zugreifen

Beachten Sie außerdem, dass wir unsereProjection im selben Paket wie unsere Modelle definieren müssen. Alternativ können wirRepositoryRestConfigurerAdapter verwenden, um es explizit hinzuzufügen:

@Configuration
public class RestConfig extends RepositoryRestConfigurerAdapter {

    @Override
    public void configureRepositoryRestConfiguration(
      RepositoryRestConfiguration repositoryRestConfiguration) {
        repositoryRestConfiguration.getProjectionConfiguration()
          .addProjection(CustomBook.class);
    }
}

4. Hinzufügen neuer Daten zu Projektionen

Lassen Sie uns nun sehen, wie Sie unserer Projektion neue Daten hinzufügen.

Wie wir im vorherigen Abschnitt besprochen haben, können wir mithilfe einer Projektion auswählen, welche Attribute in unsere Ansicht aufgenommen werden sollen. Darüber hinaus können wir Daten hinzufügen, die nicht in der Originalansicht enthalten sind.

4.1. Versteckte Daten

Standardmäßig sind IDs nicht in der ursprünglichen Ressourcenansicht enthalten.

Um die IDs im Ergebnis zu sehen, können wir das Feldidexplizit einschließen:

@Projection(
  name = "customBook",
  types = { Book.class })
public interface CustomBook {
    @Value("#{target.id}")
    long getId();

    String getTitle();
}

Jetzt soll die Ausgabe beihttp://localhost:8080/books/1?projection=\{projection name} ein:

{
  "id" : 1,
  "title" : "Animal Farm",
  "_links" : {
     ...
  }
}

Beachten Sie, dass wir auch Daten einschließen können, die mit@JsonIgnore. aus der ursprünglichen Ansicht ausgeblendet wurden

4.2. Berechnete Daten

Wir können auch neue Daten einbeziehen, die aus unseren Ressourcenattributen berechnet wurden.

Zum Beispiel können wir die Anzahl der Autoren in unsere Projektion einbeziehen:

@Projection(name = "customBook", types = { Book.class })
public interface CustomBook {

    @Value("#{target.id}")
    long getId();

    String getTitle();

    @Value("#{target.getAuthors().size()}")
    int getAuthorCount();
}

Und wir können es beihttp://localhost:8080/books/1?projection=customBook überprüfen:

{
  "id" : 1,
  "title" : "Animal Farm",
  "authorCount" : 1,
  "_links" : {
     ...
  }
}

Wenn wir normalerweise auf verwandte Ressourcen zugreifen müssen - wie in unserem Beispiel die Autoren eines Buches -, können wir die zusätzliche Anforderung vermeiden, indem wir sie explizit einschließen:

@Projection(
  name = "customBook",
  types = { Book.class })
public interface CustomBook {

    @Value("#{target.id}")
    long getId();

    String getTitle();

    List getAuthors();

    @Value("#{target.getAuthors().size()}")
    int getAuthorCount();
}

Und die endgültige Ausgabe vonProjectionist:

{
  "id" : 1,
  "title" : "Animal Farm",
  "authors" : [ {
    "name" : "George Orwell"
  } ],
  "authorCount" : 1,
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/books/1"
    },
    "book" : {
      "href" : "http://localhost:8080/books/1{?projection}",
      "templated" : true
    },
    "authors" : {
      "href" : "http://localhost:8080/books/1/authors"
    }
  }
}

Als nächstes werfen wir einen Blick auf Auszüge.

5. Auszüge

Auszüge sind Projektionen, die wir als Standardansichten auf Ressourcensammlungen anwenden.

Passen wir unsereBookRepository so an, dass diecustomBookProjection automatisch für die Erfassungsantwort verwendet werden.

Um dies zu erreichen, verwenden wir das AttributexcerptProjectionder Annotation@RepositoryRestResource:

@RepositoryRestResource(excerptProjection = CustomBook.class)
public interface BookRepository extends CrudRepository {}

Jetzt können wir sicherstellen, dasscustomBook die Standardansicht für die Büchersammlung ist, indem wirhttp://localhost:8080/books aufrufen:

{
  "_embedded" : {
    "books" : [ {
      "id" : 1,
      "title" : "Animal Farm",
      "authors" : [ {
        "name" : "George Orwell"
      } ],
      "authorCount" : 1,
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/books/1"
        },
        "book" : {
          "href" : "http://localhost:8080/books/1{?projection}",
          "templated" : true
        },
        "authors" : {
          "href" : "http://localhost:8080/books/1/authors"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/books"
    },
    "profile" : {
      "href" : "http://localhost:8080/profile/books"
    }
  }
}

Gleiches gilt für das Anzeigen von Büchern eines bestimmten Autors beihttp://localhost:8080/authors/1/books:

{
  "_embedded" : {
    "books" : [ {
      "id" : 1,
      "authors" : [ {
        "name" : "George Orwell"
      } ],
      "authorCount" : 1,
      "title" : "Animal Farm",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/books/1"
        },
        "book" : {
          "href" : "http://localhost:8080/books/1{?projection}",
          "templated" : true
        },
        "authors" : {
          "href" : "http://localhost:8080/books/1/authors"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/authors/1/books"
    }
  }
}

Wie bereits erwähnt, gelten Auszüge automatisch nur für Sammlungsressourcen. Für eine einzelne Ressource müssen wir den Parameterprojection verwenden, wie in den vorherigen Abschnitten gezeigt.

Dies liegt daran, dass es schwierig ist, die Ressource aus einer Teilansicht heraus zu aktualisieren, wenn wir die Projektionen als Standardansicht für einzelne Ressourcen anwenden.

Abschließend ist zu beachten, dassprojections and excerpts are meant for the read-only purpose.

6. Fazit

Wir haben gelernt, wie Sie mithilfe von Spring Data REST-Projektionen benutzerdefinierte Ansichten unserer Modelle erstellen. Wir haben auch gelernt, wie Auszüge als Standardansichten für Ressourcensammlungen verwendet werden.

Den vollständigen Quellcode für die Beispiele finden Sie inover on GitHub.