1. 概述
在本文中,我們將探討 Spring Data REST 的投影和片段概念。
我們將學習如何使用投影來創建我們模型自定義視圖,以及如何使用片段作為資源集合的默認視圖。
2. 我們的領域模型
首先,讓我們定義我們的領域模型: 書 和 作者。
讓我們來查看 書 實體類:
@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;
}以及 作者 模型:
@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;
}這兩個實體之間還存在多對多關係。
接下來,我們為每個模型定義標準 Spring Data REST 倉庫:
public interface BookRepository extends CrudRepository<Book, Long> {}public interface AuthorRepository extends CrudRepository<Author, Long> {}現在,我們可以通過訪問 Book 端點來使用其 ID 獲取特定 Book 的詳細信息,網址為 http://localhost:8080/books/{id}:。
{
"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"
}
}
}請注意,由於 作者 模型擁有其自身的倉庫,作者的詳細信息不包含在響應中。但是,我們可以找到指向他們的鏈接:http://localhost:8080/books/1/authors.
3. 創建投影
有時,我們只對實體屬性的子集或自定義視圖感興趣。對於這種情況,我們可以使用投影。讓我們使用 Spring Data REST 投影創建一個自定義視圖,用於我們的 Book。
我們將首先創建一個名為 CustomBook 的簡單 Projection。
@Projection(
name = "customBook",
types = { Book.class })
public interface CustomBook {
String getTitle();
}請注意,我們的投影被定義為一個具有 @Projection 註解的接口。我們可以使用 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"
}
}
}在這裏,我們只能獲取 字段,而 字段已不再存在於自定義視圖中。
一般來説,我們可以通過 訪問投影的結果。
此外,請注意,我們需要在與我們的模型相同的包中定義我們的 。 另一種方法是使用 顯式添加它。
@Configuration
public class RestConfig implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(
RepositoryRestConfiguration repositoryRestConfiguration, CorsRegistry cors) {
repositoryRestConfiguration.getProjectionConfiguration()
.addProjection(CustomBook.class);
}
}4. 添加新數據到投影
現在,讓我們看看如何向我們的投影添加新數據。
正如我們在上一部分中所討論的,我們可以使用投影來選擇包含在視圖中的屬性。 此外,我們還可以添加未包含在原始視圖中的數據。
4.1. 隱藏數據
默認情況下,ID 不包含在原始資源視圖中。
要查看結果中的 ID,可以顯式包含 <em 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();
}我們可以通過以下鏈接進行檢查:<em http://localhost:8080/books/1?projection=customBook:
{
"id" : 1,
"title" : "Animal Farm",
"authorCount" : 1,
"_links" : {
...
}
}4.3. 方便訪問相關資源
最後,如果通常需要訪問相關資源——例如,在我們的示例中,書籍的作者,我們可以通過明確包含它們來避免額外的請求:
@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();
}最終的 投影 輸出將是:
{
"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"
}
}
}接下來,我們將查看 Excerpts。
5. 摘錄
摘錄是指我們默認應用於資源集合的投影。
我們將自定義 BookRepository 以自動使用 customBook 投影。
要實現這一點,我們將使用 excerptProjection 屬性,應用於 @RepositoryRestResource 註解。
@RepositoryRestResource(excerptProjection = CustomBook.class)
public interface BookRepository extends CrudRepository<Book, Long> {}現在我們可以確保 customBook 是書籍集合的默認視圖,通過調用 http://localhost:8080/books 實現:
{
"_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 設置為單個資源的默認視圖,將難以知道如何從部分視圖中更新資源。
最後,請記住,projections 和 excerpts 僅用於只讀目的。
6. 結論
我們學習瞭如何使用 Spring Data REST 投影來創建自定義的模型視圖。我們還學習瞭如何使用摘錄作為默認視圖,用於資源集合。