此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Neo4j 7.5.2! |
唯一 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。如果你想创建一个具有内部生成的 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 属性提供一个 setter 或类似 wither 的东西
-
优点:很明显,id 属性是代理业务密钥,使用它不需要进一步的努力或配置。
-
缺点:它与 Neo4js 内部数据库 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
田被提供。