Spring Data Neo4j 擴充套件
Spring Data Neo4j Repository 的可用擴充套件
Spring Data Neo4j 提供了一些可以新增到 Repository 的擴充套件或“mixin”。什麼是 mixin?根據 Wikipedia 的說法,mixin 是一種語言概念,它允許程式設計師將一些程式碼注入到類中。Mixin 程式設計是一種軟體開發風格,其中功能單元在一個類中建立,然後與其他類混合。
Java 在語言層面不支援這個概念,但我們透過一些介面以及一個新增適當實現和攔截器的執行時來模擬它。
預設新增的 mixin 分別是 QueryByExampleExecutor
和 ReactiveQueryByExampleExecutor
。這些介面在按示例查詢中進行了詳細解釋。
提供的其他 mixin 有
-
QuerydslPredicateExecutor
-
CypherdslConditionExecutor
-
CypherdslStatementExecutor
-
ReactiveQuerydslPredicateExecutor
-
ReactiveCypherdslConditionExecutor
-
ReactiveCypherdslStatementExecutor
向生成的查詢新增動態條件
QuerydslPredicateExecutor
和 CypherdslConditionExecutor
都提供了相同的概念:SDN 生成一個查詢,你提供要新增的“謂詞”(Query DSL) 或“條件”(Cypher DSL)。我們推薦使用 Cypher DSL,因為這是 SDN 原生使用的。你甚至可能想要考慮使用註解處理器,它會為你生成一個靜態元模型。
這是如何工作的?按照上述描述宣告你的 Repository,並新增以下介面中的**一個**
interface QueryDSLPersonRepository extends
Neo4jRepository<Person, Long>, (1)
QuerydslPredicateExecutor<Person> { (2)
}
1 | 標準 Repository 宣告 |
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 | 標準 Repository 宣告 |
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 的一個優勢,後者無法做到。可以使用 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 語句執行器還提供了返回分頁結果的過載。