Handling and provisioning of unique IDs
为 Neo4j 域实体提供唯一标识符有三种主要方法:
-
使用内部生成的 ID:由 Neo4j 管理,简单易用,但与数据库 ID 绑定,并且无法生成不可变实体。
-
使用外部提供的替代密钥:由应用程序控制,生成唯一密钥,但可能在多个应用程序实例中导致重复。
-
使用业务密钥:由应用程序分配,易于理解,但难以更新,并且可能难以找到真正唯一的标识符。
Using the internal Neo4j id
为您的域类提供唯一标识符的最简单方法是将 @Id
和 @GeneratedValue
与类型为 String
或 Long
的字段结合使用(首选对象而非标量 long
,因为文字 null
是一个更好的指示符,可以判断一个实例是否是新的):
The easiest way to give your domain classes a unique identifier is the combination of @Id
and @GeneratedValue
on a field of type String
or Long
(preferable the object, not the scalar long
, as literal null
is the better indicator whether an instance is new or not):
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue
private Long id;
private String name;
public MovieEntity(String name) {
this.name = name;
}
}
您不需要为该字段提供一个设置器,SDN 将使用反射来分配该字段,但如果存在设置器,请使用该设置器。如果您希望使用内部生成 ID 创建一个不可变实体,则必须提供一个 wither。
You don’t need to provide a setter for the field, SDN will use reflection to assign the field, but use a setter if there is one. If you want to create an immutable entity with an internally generated id, you have to provide a 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 | Immutable final id field indicating a generated value |
2 | Public constructor, used by the application and Spring Data |
3 | Internally used constructor |
4 | This is a so-called wither for the id -attribute.
It creates a new entity and set’s the field accordingly, without modifying the original entity, thus making it immutable. |
如果你想提供一个设置器来设置 id 属性或者像 wither 这样的东西,则:
You either have to provide a setter for the id attribute or something like a wither, if you want to have
-
Advantages: It is pretty clear that the id attribute is the surrogate business key, it takes no further effort or configuration to use it.
-
Disadvantage: It is tied to Neo4js internal database id, which is not unique to our application entity only over a database lifetime.
-
Disadvantage: It takes more effort to create an immutable entity
Use externally provided surrogate keys
@GeneratedValue
注释可以采用一个实现 org.springframework.data.neo4j.core.schema.IdGenerator
的类作为参数。SDN 提供了 InternalIdGenerator
(默认)和 UUIDStringGenerator
开箱即用。后者为每个实体生成新的 UUID,并将其作为 java.lang.String
返回。使用它的应用程序实体看起来像这样:
The @GeneratedValue
annotation can take a class implementing org.springframework.data.neo4j.core.schema.IdGenerator
as parameter.
SDN provides InternalIdGenerator
(the default) and UUIDStringGenerator
out of the box.
The latter generates new UUIDs for each entity and returns them as java.lang.String
.
An application entity using that would look like this:
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue(UUIDStringGenerator.class)
private String id;
private String name;
}
我们必须讨论关于优势和劣势的两个不同方面。分配本身和 UUID 策略。 universally unique identifier在实践中是为了唯一。援引 Wikipedia:“因此,任何人都可以创建 UUID 并使用它来识别某物,几乎可以确定该标识符不会重复已经或将要为识别某物而创建的一个。”我们的策略使用 Java 内部 UUID 机制,采用加密强伪随机数生成器。在大多数情况下,这应该可以正常工作,但您的里程可能有所不同。
We have to discuss two separate things regarding advantages and disadvantages. The assignment itself and the UUID-Strategy. A universally unique identifier is meant to be unique for practical purposes. To quote Wikipedia: “Thus, anyone can create a UUID and use it to identify something with near certainty that the identifier does not duplicate one that has already been, or will be, created to identify something else.” Our strategy uses Java internal UUID mechanism, employing a cryptographically strong pseudo random number generator. In most cases that should work fine, but your mileage might vary.
这就剩下分配本身了:
That leaves the assignment itself:
-
Advantage: The application is in full control and can generate a unique key that is just unique enough for the purpose of the application. The generated value will be stable and there won’t be a need to change it later on.
-
Disadvantage: The generated strategy is applied on the application side of things. In those days most applications will be deployed in more than one instance to scale nicely. If your strategy is prone to generate duplicates then inserts will fail as the uniqueness property of the primary key will be violated. So while you don’t have to think about a unique business key in this scenario, you have to think more what to generate.
你有几个选项可以推出自己的 ID 生成器。一个是一个实现生成器的 POJO:
You have several options to roll out your own ID generator. One is a POJO implementing a generator:
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:
Another option is to provide an additional Spring Bean like this:
@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 | Use exactly the query or logic your need. |
上面的生成器将被配置为像这样的 bean 引用:
The generator above would be configured as a bean reference like this:
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue(generatorRef = "myIdGenerator")
private String id;
private String name;
}
Using a business key
我们一直在 MovieEntity
和 xref:object-mapping/metadata-based-mapping.adoc#mapping.complete-example.person[PersonEntity
中的完整示例中使用业务密钥。无论是在构建时还是在通过 Spring Data 加载时,该人名都会在构建时分配。
We have been using a business key in the complete example’s MovieEntity
and PersonEntity
.
The name of the person is assigned at construction time, both by your application and while being loaded through Spring Data.
这只有在你找到一个稳定的唯一业务键时才有可能,但会生成非常好的不可变域对象。
This is only possible, if you find a stable, unique business key, but makes great immutable domain objects.
-
Advantages: Using a business or natural key as primary key is natural. The entity in question is clearly identified, and it feels most of the time just right in the further modelling of your domain.
-
Disadvantages: Business keys as primary keys will be hard to update once you realise that the key you found is not as stable as you thought. Often it turns out that it can change, even when promised otherwise. Apart from that, finding identifier that are truly unique for a thing is hard.
请记住,在 Spring Data Neo4j 处理业务密钥之前,它始终会设置在域实体上。这意味着它无法确定该实体是否为新实体(它始终假设该实体为新实体),除非还提供了 xref:object-mapping/metadata-based-mapping.adoc#mapping.annotations.version[@Version
字段。
Please keep in mind that a business key is always set on the domain entity before Spring Data Neo4j processes it.
This means that it cannot determine if the entity was new or not (it always assumes that the entity is new),
unless also a @Version
field is provided.