我认为没有开箱即用的支持使用纯 JPA-2 API 使用自定义注释生成自定义 Id。但如果您想使用提供商特定的 API,那么工作就非常简单了。示例示例
要独立于提供商,请尝试以下任何技巧......
身份生成器持有者
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();
}
Specific IdGenerator - 产品 ID 生成器
public class ProductIdGenerator implements IdGenerator {
public String generate() {
/* some complicated logic goes here */
return ${generatedId};
}
}
现在设置生成的 id在无参构造函数中 OR 在@PrePersist方法中.
产品.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();
}
自定义Id.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());
}
}
项目.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字段(即代理键),我们必须使用business/natural key来实现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();
}
}
UPDATE
如果我们将 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 方法(在类中声明的方法以及在超类中声明的所有其他方法)。除非您显示一些代码和控制台,否则无法告诉您出了什么问题。