我正在迁移遗留系统以使用Hibernate 3.它目前会生成自己的标识符。为了在我尝试将其移动到更好的状态之前保持系统当前所做的事情,我将如何指定(使用注释)我自己的类,它将在插入时返回自定义生成的标识符?
类似的东西:
@Id
@CustomIdGenerator(Foo.class) // obviously this is not a real annotation
public String getId() { ... }
Foo
类有一个生成标识符的方法。
目前我只是手动调用setId(String id)
方法,但希望有更好的方法来处理这种情况。
答案 0 :(得分:7)
我不认为使用纯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};
}
}
现在将生成的ID设置为 in no-arg constructor 或 in @PrePersist method 。
<强> 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字段(即代理键),我们必须使用business / natural键来实现equals()方法。但是如果我们在no-arg构造函数中将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将被更改!!!”因此,在no-arg构造函数中设置id是可取的。或者为了解决这个问题,请进行非空检查...
@PrePersist
public void generateId() {
if(id != null)
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
如果我们将id生成放入 no-arg构造函数,不会那样 加载实体时导致问题 从数据库?因为休眠 将调用no-arg构造函数 导致现有ID出现问题 重新产生
是的,你是对的,我错过了那一部分。 :(实际上,我想告诉你: - 在我的应用程序中,每个Entity对象都与一个组织实体相关联;所以我创建了一个带有两个构造函数的抽象超类,每个实体(组织除外)都扩展了这个类。 / p>
protected PersistentEntityImpl() {
}
protected PersistentEntityImpl(Organization organization) {
String entityId = UUIDGenerator.generate();
String organizationId = organization.getEntityId();
identifier = new EntityIdentifier(entityId, organizationId);
}
no-arg构造函数用于JPA提供程序,我们从不调用no-arg构造函数,而是调用其他基于组织的构造函数。如你看到的。 id在基于组织的构造函数中分配。 (在写答案时我真的很想念这一点,对不起)。
查看您是否可以在应用程序中实施此策略或类似策略。
第二种选择是使用 @PrePersist注释。我把它放进去了 而且这个方法永远不会被击中并给予 我是一个例外,说明我需要 手动设置id。在那儿 我还应该做些什么?
理想情况下,JPA提供程序应该在持久化实体对象之前调用@PrePersist方法(在类中声明的方法以及在超类中声明的所有其他方法)。除非你展示一些代码和控制台,否则不能告诉你出了什么问题。
答案 1 :(得分:1)
你可以。
首先,实施org.hibernate.id.IdentifierGenerator
然后你必须将它映射到映射xml文件中。我找不到用注释做到这一点的方法:
<!--
<identifier-generator.../> allows customized short-naming
of IdentifierGenerator implementations.
-->
<!ELEMENT identifier-generator EMPTY>
<!ATTLIST identifier-generator name CDATA #REQUIRED>
<!ATTLIST identifier-generator class CDATA #REQUIRED>
最后,使用@GeneratedValue(generator="identifier-name")
请注意,这是特定于休眠的(不是JPA)
更新:我看了一下Hibernate的来源,似乎在一个地方,在未能解析短名称后,hibernates尝试调用Class.forName(..)
。那里的参数叫做strategy
。所以这是你尝试的:
generator
属性@GenericGenerator
strategy
属性中的字符串(带有一些任意名称)让我知道哪些(如果有的话)有效