此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Neo4j 7.4.4! |
处理和预置唯一 ID
使用内部 Neo4j ID
为域类提供唯一标识符的最简单方法是@Id
和@GeneratedValue
在类型为String
或Long
(最好是对象,而不是标量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,则使用 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 | 指示生成值的不可变最终 ID 字段 |
2 | 应用程序和 Spring Data 使用的公共构造函数 |
3 | 内部使用的构造函数 |
4 | 这就是所谓的id -属性。
它创建一个新实体并相应地设置字段,而无需修改原始实体,从而使其不可变。 |
如果你想让
-
优点: 很明显,id 属性是代理业务密钥,使用它不需要进一步的努力或配置。
-
缺点:它与 Neo4js 内部数据库 ID 相关联,这并不是我们的应用程序实体仅在数据库生命周期内唯一的唯一。
-
缺点:创建不可变实体需要花费更多精力
使用外部提供的代理键
这@GeneratedValue
注解可以接受一个类 implementationorg.springframework.data.neo4j.core.schema.IdGenerator
as 参数。
SDN 提供InternalIdGenerator
(默认值)和UUIDStringGenerator
开箱即用。
后者为每个实体生成新的 UUID,并将其作为java.lang.String
.
使用该 API 的应用程序实体将如下所示:
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue(UUIDStringGenerator.class)
private String id;
private String name;
}
关于优点和缺点,我们必须讨论两件不同的事情。 赋值本身和 UUID-Strategy. 通用唯一标识符在实际应用中是唯一的。 引用维基百科: “因此,任何人都可以创建一个 UUID 并使用它来识别某些东西,几乎可以肯定该标识符不会与已经或将要创建的标识符重复以识别其他东西。”我们的策略使用 Java 内部 UUID 机制,采用密码学强的伪随机数生成器。 在大多数情况下,这应该可以正常工作,但您的里程可能会有所不同。
这就剩下了赋值本身:
-
优点:应用程序处于完全控制之中,并且可以生成一个唯一密钥,该密钥对于应用程序的目的来说足够唯一。 生成的值将是稳定的,以后不需要更改它。
-
缺点:生成的策略应用于 Thing 的应用程序端。 在那些日子里,大多数应用程序将部署在多个实例中,以便很好地扩展。 如果您的策略容易生成重复项,则插入将失败,因为将违反主键的唯一性属性。 因此,虽然在这种情况下您不必考虑唯一的业务密钥,但您必须更多地考虑要生成什么。
您可以通过多种方式推出自己的 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();
}
}
Another option is to provide an additional Spring Bean like this:
Example 5. Neo4jClient based ID generator
@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.
The generator above would be configured as a bean reference like this:
Example 6. Mutable MovieEntity using a Spring Bean as Id generator
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue(generatorRef = "myIdGenerator")
private String id;
private String name;
}
Using a business key
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.
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.