Spring Data Neo4j 擴充功能
Spring Data Neo4j 儲存庫的可用擴充功能
Spring Data Neo4j 提供了一些可以新增到儲存庫的擴充功能或「mixin」。什麼是 mixin?根據維基百科,mixin 是一種語言概念,允許程式設計師將一些程式碼注入到類別中。Mixin 程式設計是一種軟體開發風格,其中功能單元在類別中建立,然後與其他類別混合使用。
Java 在語言層級上不支援這個概念,但我們透過一些介面和執行階段來模擬它,以便為其新增適當的實作和攔截器。
預設新增的 Mixin 分別是 QueryByExampleExecutor
和 ReactiveQueryByExampleExecutor
。這些介面在範例查詢中詳細說明。
提供的其他 mixin 為
-
QuerydslPredicateExecutor
-
CypherdslConditionExecutor
-
CypherdslStatementExecutor
-
ReactiveQuerydslPredicateExecutor
-
ReactiveCypherdslConditionExecutor
-
ReactiveCypherdslStatementExecutor
將動態條件新增至產生的查詢
QuerydslPredicateExecutor
和 CypherdslConditionExecutor
都提供相同的概念:SDN 產生查詢,您提供將被新增的「述詞」(Query DSL) 或「條件」(Cypher DSL)。我們建議使用 Cypher DSL,因為這是 SDN 原生使用的語言。您甚至可以考慮使用註解處理器,它會為您產生靜態中繼模型。
它是如何運作的?如上所述宣告您的儲存庫,並新增下列其中一個介面
interface QueryDSLPersonRepository extends
Neo4jRepository<Person, Long>, (1)
QuerydslPredicateExecutor<Person> { (2)
}
1 | 標準儲存庫宣告 |
2 | Query DSL mixin |
或
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.support.CypherdslConditionExecutor;
interface PersonRepository extends
Neo4jRepository<Person, Long>, (1)
CypherdslConditionExecutor<Person> { (2)
}
1 | 標準儲存庫宣告 |
2 | Cypher DSL mixin |
範例用法以 Cypher DSL 條件執行器顯示
Node person = Cypher.node("Person").named("person"); (1)
Property firstName = person.property("firstName"); (2)
Property lastName = person.property("lastName");
assertThat(
repository.findAll(
firstName.eq(Cypher.anonParameter("Helge"))
.or(lastName.eq(Cypher.parameter("someName", "B."))), (3)
lastName.descending() (4)
))
.extracting(Person::getFirstName)
.containsExactly("Helge", "Bela");
1 | 定義名為 Node 的物件,以查詢的根目錄為目標 |
2 | 從中衍生一些屬性 |
3 | 建立 or 條件。匿名參數用於名字,具名參數用於姓氏。這就是您在這些片段中定義參數的方式,也是優於 Query-DSL mixin 的優點之一,Query-DSL mixin 無法做到這一點。可以使用 Cypher.literalOf 表示常值。 |
4 | 從其中一個屬性定義 SortItem |
Query-DSL mixin 的程式碼看起來非常相似。使用 Query-DSL mixin 的原因可能是 API 的熟悉度,以及它也適用於其他儲存區。反對它的原因是您需要在類別路徑上新增一個額外的程式庫,它缺少對遍歷關係的支援,以及上面提到的事實,即它在其述詞中不支援參數 (技術上是支援的,但沒有 API 方法可以實際將它們傳遞到正在執行的查詢)。
針對實體和投影使用 (動態) Cypher-DSL 語句
新增對應的 mixin 與使用條件執行器沒有什麼不同
interface PersonRepository extends
Neo4jRepository<Person, Long>,
CypherdslStatementExecutor<Person> {
}
擴充 ReactiveNeo4jRepository
時,請使用 ReactiveCypherdslStatementExecutor
。
CypherdslStatementExecutor
提供了幾個 findOne
和 findAll
的多載。它們都採用 Cypher-DSL 語句,分別是以該語句的進行中定義作為第一個參數,並且在投影方法的情況下,採用類型。
如果查詢需要參數,則必須透過 Cypher-DSL 本身定義,並由其填充,如下面的清單所示
static Statement whoHasFirstNameWithAddress(String name) { (1)
Node p = Cypher.node("Person").named("p"); (2)
Node a = Cypher.anyNode("a");
Relationship r = p.relationshipTo(a, "LIVES_AT");
return Cypher.match(r)
.where(p.property("firstName").isEqualTo(Cypher.anonParameter(name))) (3)
.returning(
p.getRequiredSymbolicName(),
Cypher.collect(r),
Cypher.collect(a)
)
.build();
}
@Test
void fineOneShouldWork(@Autowired PersonRepository repository) {
Optional<Person> result = repository.findOne(whoHasFirstNameWithAddress("Helge")); (4)
assertThat(result).hasValueSatisfying(namesOnly -> {
assertThat(namesOnly.getFirstName()).isEqualTo("Helge");
assertThat(namesOnly.getLastName()).isEqualTo("Schneider");
assertThat(namesOnly.getAddress()).extracting(Person.Address::getCity)
.isEqualTo("Mülheim an der Ruhr");
});
}
@Test
void fineOneProjectedShouldWork(@Autowired PersonRepository repository) {
Optional<NamesOnly> result = repository.findOne(
whoHasFirstNameWithAddress("Helge"),
NamesOnly.class (5)
);
assertThat(result).hasValueSatisfying(namesOnly -> {
assertThat(namesOnly.getFirstName()).isEqualTo("Helge");
assertThat(namesOnly.getLastName()).isEqualTo("Schneider");
assertThat(namesOnly.getFullName()).isEqualTo("Helge Schneider");
});
}
1 | 動態查詢以類型安全的方式在輔助方法中建置 |
2 | 我們已經在這裡看到了這一點,我們也在這裡定義了一些變數來保存模型 |
3 | 我們定義了一個匿名參數,由傳遞給方法的 name 的實際值填充 |
4 | 從輔助方法傳回的語句用於尋找實體 |
5 | 或投影。 |
findAll
方法的工作方式類似。命令式 Cypher-DSL 語句執行器也提供了傳回分頁結果的多載。