唯一 ID 的處理與佈建

使用 Neo4j 內部 ID

為您的領域類別提供唯一識別碼最簡單的方式,是在型別為 StringLong 的欄位上組合使用 @Id@GeneratedValue(最好是物件型別,而非純量型別 long,因為字面值 null 更能指示實例是否為新的)

範例 1. 具有內部 Neo4j ID 的可變 MovieEntity
@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

範例 2. 具有內部 Neo4j ID 的不可變 MovieEntity
@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 傳回。使用它的應用程式實體看起來會像這樣

範例 3. 具有外部產生的代理鍵的可變 MovieEntity
@Node("Movie")
public class MovieEntity {

	@Id @GeneratedValue(UUIDStringGenerator.class)
	private String id;

	private String name;
}

我們必須討論關於優點和缺點的兩個獨立事項。賦值本身和 UUID 策略。通用唯一識別碼旨在用於實務目的的唯一性。引用維基百科:「因此,任何人都可以建立一個 UUID 並用它來識別某事物,並幾乎可以確定該識別碼不會重複已建立或將要建立的用於識別其他事物的識別碼。」我們的策略使用 Java 內部 UUID 機制,採用密碼學上強大的偽隨機數產生器。在大多數情況下,這應該可以正常運作,但您的里程可能會有所不同。

這留下了賦值本身

  • 優點:應用程式完全掌控,可以產生一個唯一鍵,該鍵對於應用程式的目的而言足夠唯一。產生的值將是穩定的,並且以後不需要更改它。

  • 缺點:產生的策略應用於應用程式端。在當今時代,大多數應用程式將部署在多個實例中以實現良好的擴展。如果您的策略容易產生重複項,則插入將會失敗,因為主要鍵的唯一性屬性將會被違反。因此,雖然您不必在這個情境中考慮唯一的業務金鑰,但您必須更多地思考要產生什麼。

您有多種選項可以推出自己的 ID 產生器。其中一種是實作產生器的 POJO

範例 4. 樸素的序列產生器
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,如下所示

範例 5. 基於 Neo4jClient 的 ID 產生器
@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 參考

範例 6. 使用 Spring Bean 作為 Id 產生器的可變 MovieEntity
@Node("Movie")
public class MovieEntity {

	@Id @GeneratedValue(generatorRef = "myIdGenerator")
	private String id;

	private String name;
}

使用業務金鑰

我們一直在完整的範例 MovieEntityPersonEntity 中使用業務金鑰。人員的名稱在建構時指派,既由您的應用程式指派,也在透過 Spring Data 載入時指派。

只有當您找到穩定的唯一業務金鑰時,這才有可能,但可以建立出色的不可變領域物件。

  • 優點:使用業務金鑰或自然金鑰作為主要金鑰是很自然的。所討論的實體被清楚地識別,並且在進一步的領域建模中,大多數時候感覺都很正確。

  • 缺點:一旦您意識到您找到的金鑰不如您想像的那麼穩定,作為主要金鑰的業務金鑰將很難更新。通常情況是,即使承諾另有說法,它也可能會改變。除此之外,為事物找到真正唯一的識別碼是很困難的。

請記住,業務金鑰始終在 Spring Data Neo4j 處理它之前在領域實體上設定。這表示它無法確定實體是否為新的(它始終假定實體是新的),除非也提供了 @Version 欄位