知識庫 / Spring / Spring Data RSS 訂閱

Spring Data REST 中的投影與片段

REST,Spring Data
HongKong
6
03:53 AM · Dec 06 ,2025

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 投影來創建自定義的模型視圖。我們還學習瞭如何使用摘錄作為默認視圖,用於資源集合。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.