唯一 ID 的處理和提供
使用內部 Neo4j ID
為您的域類提供唯一識別符號的最簡單方法是在 String 或 Long 型別的欄位上結合使用 @Id 和 @GeneratedValue(最好是物件型別,而不是標量 long,因為字面量 null 是判斷例項是否為新的更好指標)。
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue
private Long id;
private String name;
public MovieEntity(String name) {
this.name = name;
}
}
您不需要為該欄位提供 setter,SDN 將使用反射來賦值,但如果存在 setter,則會使用它。如果您想建立一個具有內部生成 ID 的不可變實體,則必須提供一個 wither。
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue
private final Long id; (1)
private String name;
public MovieEntity(String name) { (2)
this(null, name);
}
private MovieEntity(Long id, String name) { (3)
this.id = id;
this.name = name;
}
public MovieEntity withId(Long id) { (4)
if (this.id.equals(id)) {
return this;
} else {
return new MovieEntity(id, this.title);
}
}
}
| 1 | 指示生成值的不可變 final ID 欄位 |
| 2 | 公共建構函式,由應用程式和 Spring Data 使用 |
| 3 | 內部使用的建構函式 |
| 4 | 這是 id 屬性的所謂 wither。它建立一個新實體並相應地設定欄位,而不修改原始實體,從而使其不可變。 |
如果您想要,則必須為 ID 屬性提供 setter 或類似 wither 的東西。
-
優點:ID 屬性是替代業務鍵非常明確,使用它不需要額外的努力或配置。
-
缺點:它與 Neo4j 的內部資料庫 ID 繫結,這在資料庫生命週期內不一定僅對我們的應用程式實體是唯一的。
-
缺點:建立不可變實體需要更多努力
使用外部提供的代理鍵
@GeneratedValue 註解可以接受一個實現 org.springframework.data.neo4j.core.schema.IdGenerator 介面的類作為引數。SDN 開箱即用提供了 InternalIdGenerator(預設)和 UUIDStringGenerator。後者為每個實體生成新的 UUID 並將其作為 java.lang.String 返回。使用它的應用程式實體將如下所示:
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue(UUIDStringGenerator.class)
private String id;
private String name;
}
我們需要討論關於優點和缺點的兩個不同方面。賦值本身和 UUID 策略。一個 通用唯一識別符號 旨在在實際應用中是唯一的。引用維基百科:“因此,任何人都可以建立一個 UUID 並使用它來識別某物,幾乎可以肯定該識別符號不會與已經建立或將要建立的用於識別其他事物的識別符號重複。” 我們的策略使用 Java 內部的 UUID 機制,採用密碼學上強大的偽隨機數生成器。在大多數情況下,這應該沒有問題,但效果可能會因情況而異。
這就引出了賦值本身的問題
-
優點:應用程式完全可控,可以生成一個對於應用程式目的來說足夠唯一的鍵。生成的值將是穩定的,並且以後不需要更改。
-
缺點:生成策略是在應用程式端應用的。如今,大多數應用程式將部署在多個例項中以實現良好的擴充套件性。如果您的策略容易生成重複項,那麼插入將失敗,因為主鍵的唯一性屬性將被違反。因此,在這種情況下,雖然您不必考慮唯一的業務鍵,但您必須更多地考慮要生成什麼。
您有幾種選項可以實現自己的 ID 生成器。其中之一是實現生成器的 POJO。
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.data.neo4j.core.schema.IdGenerator;
import org.springframework.util.StringUtils;
public class TestSequenceGenerator implements IdGenerator<String> {
private final AtomicInteger sequence = new AtomicInteger(0);
@Override
public String generateId(String primaryLabel, Object entity) {
return StringUtils.uncapitalize(primaryLabel) +
"-" + sequence.incrementAndGet();
}
}
另一個選項是提供一個額外的 Spring Bean,如下所示:
@Component
class MyIdGenerator implements IdGenerator<String> {
private final Neo4jClient neo4jClient;
public MyIdGenerator(Neo4jClient neo4jClient) {
this.neo4jClient = neo4jClient;
}
@Override
public String generateId(String primaryLabel, Object entity) {
return neo4jClient.query("YOUR CYPHER QUERY FOR THE NEXT ID") (1)
.fetchAs(String.class).one().get();
}
}
| 1 | 準確使用您需要的查詢或邏輯。 |
上述生成器將被配置為一個 Bean 引用,如下所示:
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue(generatorRef = "myIdGenerator")
private String id;
private String name;
}
使用業務鍵
我們在完整示例的 MovieEntity 和 PersonEntity 中使用了業務鍵。人物的名稱在構建時分配,無論是透過您的應用程式還是透過 Spring Data 載入時。
這隻有在您找到一個穩定、唯一的業務鍵時才可能實現,但它能創建出色的不可變域物件。
-
優點:使用業務鍵或自然鍵作為主鍵是很自然的。所討論的實體被清晰地標識,並且在進一步的領域建模中,它在大多數情況下都感覺非常合適。
-
缺點:當您發現所找到的鍵不如您想象的穩定時,作為主鍵的業務鍵將很難更新。通常事實證明它會改變,即使承諾不會。除此之外,找到真正唯一的識別符號也很困難。
請記住,業務鍵總是在 Spring Data Neo4j 處理之前在域實體上設定。這意味著它無法確定實體是新的還是舊的(它總是假設實體是新的),除非還提供了 @Version 欄位。