我认为使用纯 JPA-2 API 使用自定义注释生成自定义 ID 没有开箱即用的支持。但是如果你想使用提供者特定的 API,那么工作就很简单了。 Sample Example
要独立于提供者,请尝试以下任何技巧....
IdGeneratorHolder
public abstract class IdGeneratorHolder {
/* PersistentEntity is a marker interface */
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
/* sample impelementation */
if(Product.class.isAssignableFrom(entityType)) {
return new ProductIdGenerator();
}
return null;
}
}
通用 IdGenerator 界面
public interface IdGenerator {
String generate();
}
特定 IdGenerator - 产品 ID 生成器
public class ProductIdGenerator implements IdGenerator {
public String generate() {
/* some complicated logic goes here */
return ${generatedId};
}
}
现在在无参数构造函数或@PrePersist方法中设置生成的ID。
Product.java
public class Product implements PersistentEntity {
private String id;
public Product() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
@PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
在上面的例子中,所有的 id 都是相同的类型,即java.lang.String。如果持久实体有不同类型的 id......
IdGenerator.java
public interface IdGenerator {
CustomId generate();
}
CustomId.java
public class CustomId {
private Object id;
public CustomId(Object id) {
this.id = id;
}
public String toString() {
return id.toString();
}
public Long toLong() {
return Long.valueOf(id.toString());
}
}
Item.java
@PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate().toLong();
}
您也可以使用自定义注释...
CustomIdGenerator.java
public @interface CustomIdGenerator {
IdStrategy strategy();
}
IdStrategy.java
enum IdStrategy {
uuid, humanReadable,
}
IdGeneratorHolder.java
public abstract class IdGeneratorHolder {
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
try { // again sample implementation
Method method = entityType.getMethod("idMethod");
CustomIdGenerator gen = method.getAnnotation(CustomIdGenerator.class);
IdStrategy strategy = gen.strategy();
return new ProductIdGenerator(strategy);
}
还有一件事......如果我们在@PrePersist方法中设置id,equals()方法不能依赖id字段(即代理键),我们必须使用业务/自然键来实现equals()方法。但是如果我们在无参数构造函数中将 id 字段设置为某个唯一值(uuid 或“app-uid”在应用程序中唯一),它有助于我们实现 equals() 方法。
public boolean equals(Object obj) {
if(obj instanceof Product) {
Product that = (Product) obj;
return this.id ==that.id;
}
return false;
}
如果我们或其他人多次调用(有意或错误地)@PrePersist 注解的方法,“唯一 id 将被更改!!!”所以在无参数构造函数中设置 id 是可取的。或者要解决这个问题,请进行非空检查...
@PrePersist
public void generateId() {
if(id != null)
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
更新
如果我们将 id 生成放在一个
没有参数的构造函数,不是吗
加载实体时导致问题
从数据库?因为冬眠
将调用无参数构造函数
导致现有的 id
重新生成
是的,你是对的,我错过了那部分。 :( 实际上,我想告诉你:- 在我的应用程序中,每个实体对象都与一个组织实体相关联;所以我创建了一个带有两个构造函数的抽象超类,每个实体(组织除外)都扩展了这个类。
protected PersistentEntityImpl() {
}
protected PersistentEntityImpl(Organization organization) {
String entityId = UUIDGenerator.generate();
String organizationId = organization.getEntityId();
identifier = new EntityIdentifier(entityId, organizationId);
}
无参数构造函数用于 JPA 提供者,我们从不调用无参数构造函数,而是调用其他基于组织的构造函数。如你看到的。 id 在基于组织的构造函数中分配。 (我在写答案时真的错过了这一点,对此感到抱歉)。
看看你是否可以在你的应用程序中实现这个或类似的策略。
第二个选项是使用
@PrePersist 注释。我把它放进去
而且该方法从未受到打击并给予
我是一个例外,说明我需要
手动设置 id。在那儿
还有什么我应该做的吗?
理想情况下,JPA 提供者应该在持久化实体对象之前调用@PrePersist 方法(在类中声明的方法以及在超类中声明的所有其他方法)。除非您显示一些代码和控制台,否则无法告诉您出了什么问题。