Spring Data RESTにおける予測と抜粋

Spring Data RESTの予測と抜粋

1. 概要

この記事では、Spring DataRESTのプロジェクションと抜粋の概念について説明します。

use projections to create custom views of our models and how to use excerpts as default views to resource collectionsの方法を学びます。

2. 私たちのドメインモデル

まず、ドメインモデルを定義することから始めましょう:BookAuthor.

Bookのエンティティクラスを見てみましょう。

@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;
}

そして、Authorモデル:

@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;
}

また、2つのエンティティには多対多の関係があります。

次に、モデルごとに標準のSpring DataRESTリポジトリを定義しましょう。

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

これで、Bookエンドポイントにアクセスして、http://localhost:8080/books/{id}:のIDを使用して特定のBook’sの詳細を取得できます。

{
  "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"
    }
  }
}

Authorモデルにはリポジトリがあるため、作成者の詳細は応答の一部ではないことに注意してください。 ただし、それらへのリンクは見つかります–http://localhost:8080/books/1/authors.

3. 投影の作成

時々、we’re only interested in a subset or a custom view of an entity’s attributes。 そのような場合、投影法を利用できます。

Spring Data RESTプロジェクションを使用して、Bookのカスタムビューを作成しましょう。

まず、CustomBookと呼ばれる単純なProjectionを作成します。

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

our projection is defined as an interface with an @Projection annotationに注意してください。 name属性を使用してプロジェクションの名前をカスタマイズしたり、types属性を使用して適用するオブジェクトを定義したりできます。

この例では、CustomBookプロジェクションには本のtitleのみが含まれます。

Projection:を作成した後、Bookの表現をもう一度見てみましょう。

{
  "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"
    }
  }
}

素晴らしい、投影へのリンクを見ることができます。 http://localhost:8080/books/1?projection=customBookで作成したビューを確認しましょう。

{
  "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"
    }
  }
}

ここでは、titleフィールドのみを取得し、isbnはカスタムビューに存在しなくなっていることがわかります。

原則として、http://localhost:8080/books/1?projection=\{projection name}.で投影法の結果にアクセスできます

また、モデルと同じパッケージでProjectionを定義する必要があることに注意してください。 または、RepositoryRestConfigurerAdapterを使用して明示的に追加することもできます。

@Configuration
public class RestConfig extends RepositoryRestConfigurerAdapter {

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

4. 射影への新しいデータの追加

それでは、新しいデータをプロジェクションに追加する方法を見てみましょう。

前のセクションで説明したように、投影を使用して、ビューに含める属性を選択できます。 さらに、元のビューに含まれていないデータを追加することもできます。

4.1. 隠しデータ

デフォルトでは、IDは元のリソースビューに含まれていません。

結果にIDを表示するには、idフィールドを明示的に含めることができます。

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

    String getTitle();
}

これで、http://localhost:8080/books/1?projection=\{projection name} での出力は次のようになります。

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

元のビューから非表示にされたデータを@JsonIgnore.で含めることもできることに注意してください

4.2. 計算データ

リソース属性から計算された新しいデータを含めることもできます。

たとえば、著者数をProjectionに含めることができます。

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

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

    String getTitle();

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

そして、http://localhost:8080/books/1?projection=customBookで確認できます。

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

最後に、通常、関連するリソースにアクセスする必要がある場合(この例では本の著者のように)、明示的に含めることで余分なリクエストを回避できます。

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

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

    String getTitle();

    List getAuthors();

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

そして、最終的なProjection出力は次のようになります。

{
  "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"
    }
  }
}

次に、抜粋を見ていきます。

5. 抜粋

抜粋は、リソースコレクションのデフォルトビューとして適用される予測です。

BookRepositoryをカスタマイズして、コレクションの応答にcustomBookProjectionを自動的に使用してみましょう。

これを実現するために、@RepositoryRestResourceアノテーションのexcerptProjection属性を使用します。

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

これで、http://localhost:8080/booksを呼び出すことで、customBookが書籍コレクションのデフォルトビューであることを確認できます。

{
  "_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"
    }
  }
}

同じことが、http://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"
    }
  }
}

前述のように、抜粋はコレクションリソースにのみ自動的に適用されます。 単一のリソースの場合、前のセクションで示したように、projectionパラメータを使用する必要があります。

これは、投影を単一のリソースのデフォルトビューとして適用すると、部分ビューからリソースを更新する方法を知ることが難しくなるためです。

最後に、projections and excerpts are meant for the read-only purposeを覚えておくことが重要です。

6. 結論

Spring Data RESTプロジェクションを使用してモデルのカスタムビューを作成する方法を学びました。 また、リソースコレクションのデフォルトビューとして抜粋を使用する方法も学びました。

例の完全なソースコードはover on GitHubにあります。