Projections and Excerpts in Spring Data REST – Spring Data REST中的预测和摘录

最后修改: 2018年 5月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article – we’ll explore Spring Data REST’s concepts of projections and excerpts.

在这篇文章中–我们将探讨Spring Data REST的投影和摘录的概念。

We’ll learn how to use projections to create custom views of our models and how to use excerpts as default views to resource collections.

我们将学习如何使用投影来创建我们模型的自定义视图,以及如何使用摘录作为资源集合的默认视图

2. Our Domain Models

2.我们的领域模型

First, let’s start by defining our domain models: Book and Author.

首先,让我们从定义我们的领域模型开始。作者

Let’s have a look at the Book entity class:

让我们看一下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<Author> authors;
}

And the Author model:

还有作者模式。

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

The two entities also have a many-to-many relationship.

这两个实体也有一个多对多的关系。

Next, let’s define standard Spring Data REST repositories for each of the models:

接下来,让我们为每个模型定义标准的Spring Data REST存储库。

public interface BookRepository extends CrudRepository<Book, Long> {}
public interface AuthorRepository extends CrudRepository<Author, Long> {}

Now, we can access Book endpoint to get a specific Book’s details using its id at http://localhost:8080/books/{id}:

现在,我们可以访问Book端点,通过http://localhost:8080/books/{id}:的id,获得特定Book的细节。

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

Note that since the Author model has its repository, authors details aren’t part of the response. We can, however, find the link to them – http://localhost:8080/books/1/authors.

请注意,由于Author模型有它的存储库,作者的详细信息不是响应的一部分。然而,我们可以找到它们的链接 – http://localhost:8080/books/1/authors.

3. Creating a Projection

3.创建一个投影

Sometimes, we’re only interested in a subset or a custom view of an entity’s attributes. For such cases, we can make use of projections.

有时,我们只对一个实体属性的子集或自定义视图感兴趣。对于这种情况,我们可以利用投影。

Let’s create a custom view to our Book using Spring Data REST projections.

让我们使用Spring Data REST投影为我们的Book创建一个自定义视图。

We’ll start by creating a simple Projection called CustomBook:

我们将首先创建一个简单的Projection,称为CustomBook

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

Note that our projection is defined as an interface with an @Projection annotation. We can use the name attribute to customize the name of the projection, as well as the types attributes to define the objects it applies to.

请注意,我们的投影被定义为一个带有@Projection注解的接口。我们可以使用name属性来定制投影的名称,也可以使用types属性来定义它适用的对象。

In our example, the CustomBook projection will only include the title of a book.

在我们的例子中,CustomBook投影将只包括一本书的title

Let’s have a look again at our Book representation after creating our Projection:

让我们再看看我们的在创建投影后的表现:

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

Great, we can see a link to our projection. Let’s check the view we created at http://localhost:8080/books/1?projection=customBook:

很好,我们可以看到一个链接到我们的投影。让我们检查一下我们在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"
    }
  }
}

Here, we can see that we only get the title field, while the isbn is no longer present in the custom view.

在这里,我们可以看到,我们只得到了title字段,而isbn不再出现在自定义视图中。

As a general rule, we can access a projection’s result at http://localhost:8080/books/1?projection={projection name}.

一般来说,我们可以在http://localhost:8080/books/1?projection={projection name}.访问一个投影的结果。

Also, note that we need to define our Projection in the same package as our models. Alternatively, we can use RepositoryRestConfigurerAdapter to add it explicitly:

另外,请注意,我们需要将我们的Projection与我们的模型定义在同一个包中。另外,我们可以使用RepositoryRestConfigurerAdapter来明确添加它。

@Configuration
public class RestConfig implements RepositoryRestConfigurer {
 
    @Override
    public void configureRepositoryRestConfiguration(
      RepositoryRestConfiguration repositoryRestConfiguration, CorsRegistry cors) {
        repositoryRestConfiguration.getProjectionConfiguration()
          .addProjection(CustomBook.class);
    }
}

4. Adding New Data to Projections

4.在预测中添加新数据

Now, let’s see how to add new data to our projection.

现在,让我们看看如何将新数据添加到我们的预测中。

As we discussed in the previous section, we can use a Projection to select which attributes to include in our view. What’s more, we can also add data that are not included in the original view.

正如我们在上一节所讨论的,我们可以使用投影来选择在我们的视图中包含哪些属性。更重要的是,我们还可以添加原始视图中不包含的数据。

4.1. Hidden Data

4.1.隐藏的数据

By default, ids are not included in the original resource view.

默认情况下,ID不包括在原始资源视图中。

To see the ids in the result, we can include the id field explicitly:

为了在结果中看到id,我们可以明确包括id字段。

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

Now the output at http://localhost:8080/books/1?projection={projection name} will be:

现在,http://localhost:8080/books/1?projection={projection name}的输出将是。

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

Note that we can also include data that were hidden from the original view with @JsonIgnore.

请注意,我们也可以用@JsonIgnore.包括那些被隐藏在原始视图中的数据。

4.2. Calculated Data

4.2.计算的数据

We can also include new data calculated from our resource attributes.

我们还可以包括从我们的资源属性计算出来的新数据。

For example, we can include the authors count in our Projection:

例如,我们可以在我们的预测中包括作者人数。

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

And we can check it at http://localhost:8080/books/1?projection=customBook:

而我们可以在http://localhost:8080/books/1?projection=customBook上查看。

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

4.3. Easy Access to Related Resources

4.3.易于获取相关资源

Finally, if we usually need to access related resources – like in our example a book’s authors, we can avoid the extra request by including it explicitly:

最后,如果我们通常需要访问相关的资源–比如在我们的例子中,一本书的作者,我们可以通过明确包括它来避免额外的请求。

@Projection(
  name = "customBook", 
  types = { Book.class }) 
public interface CustomBook {
 
    @Value("#{target.id}")
    long getId(); 
    
    String getTitle();
    
    List<Author> getAuthors();
    
    @Value("#{target.getAuthors().size()}")
    int getAuthorCount();
}

And the final Projection output will be:

而最终的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"
    }
  }
}

Next, we’ll take a look at Excerpts.

接下来,我们将看一下摘录。

5. Excerpts

5.节选

Excerpts are projections which we apply as default views to resource collections.

摘录是我们作为默认视图应用于资源集合的预测。

Let’s customize our BookRepository to use the customBook Projection automatically for the collection response.

让我们自定义我们的BookRepository,以自动使用customBook Projection进行集合响应。

To achieve this, we’ll use the excerptProjection attribute of the @RepositoryRestResource annotation:

为了实现这一点,我们将使用@RepositoryRestResource注解的excerptProjection属性。

@RepositoryRestResource(excerptProjection = CustomBook.class)
public interface BookRepository extends CrudRepository<Book, Long> {}

Now we can make sure that customBook is the default view for the books collection by calling http://localhost:8080/books:

现在我们可以通过调用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"
    }
  }
}

The same applies to viewing books by a specific author at http://localhost:8080/authors/1/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"
    }
  }
}

As mentioned, excerpts apply automatically only to collection resources. For a single resource, we have to use the projection parameter as shown in the previous sections.

如前所述,摘录只自动适用于集合资源。对于单个资源,我们必须使用projection参数,如前几节所示。

This is because if we apply the Projections as the default view for single resources, it will make it difficult to know how to update the resource from a partial view.

这是因为如果我们应用投影作为单个资源的默认视图,将使我们很难知道如何从部分视图中更新资源。

As a final note, it’s important to remember that projections and excerpts are meant for the read-only purpose.

作为最后一点,重要的是要记住,预测和摘录是为了只读的目的

6. Conclusion

6.结论

We learned how to use Spring Data REST projections to create custom views of our models. We also learned how to use excerpts as default views to resource collections.

我们学习了如何使用Spring Data REST投影来创建我们模型的自定义视图。我们还学习了如何使用摘录作为资源集合的默认视图。

The full source code for the examples can be found over on GitHub.

这些例子的完整源代码可以在GitHub上找到over