我正在迁移旧系统以使用Hibernate3。它当前会生成自己的标识符。为了在尝试将其移至更好一点之前,保持系统当前的功能,我将如何指定(使用注释)我自己的类,当发生插入时,该类将返回定制生成的标识符?
就像是:
@Id @CustomIdGenerator(Foo.class) // obviously this is not a real annotation public String getId() { ... }
其中Foo类具有生成所述标识符的一种方法。
Foo
目前,我只是setId(String id)手动调用该方法,但希望找到一种更好的方法来处理这种情况。
setId(String id)
我认为不存在使用纯JPA-2 API使用自定义注释生成自定义ID的现成支持。但是,如果要使用提供程序特定的API,则工作非常简单。样例
要独立于提供程序,请尝试以下任何技巧。
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 。
产品.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 .....
java.lang.String
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()方法。但是,如果我们在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。或为解决此问题,请放置不为null的检查…
@PrePersist public void generateId() { if(id != null) id = IdGeneratorHolder.getIdGenerator(getClass()).generate(); } }
如果将id生成放入无参数构造函数中,那么从数据库中加载实体时不会造成问题吗?因为hibernate将调用no-arg构造函数,导致现有ID重新生成
是的,您是对的,我错过了这一部分。:(实际上,我想告诉您:-在我的应用程序中,每个Entity对象都与Organization Entity相关联;因此,我创建了一个具有两个构造函数的抽象超类,每个Entity(除Organization之外)都扩展了该类。
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方法(在类中声明的一个方法,以及在超类中声明的所有其他方法)。除非显示一些代码和控制台,否则无法告诉您出什么问题了。