将自定义标识符分配给@id属性

我正在迁移旧系统以使用Hibernate 3.它目前生成自己的标识符。 为了在我尝试将其移动到更好的状态之前保持系统当前所执行的操作,我将如何指定(使用注释)我自己的类,它将在插入时返回自定义生成的标识符?

就像是:

@Id @CustomIdGenerator(Foo.class) // obviously this is not a real annotation public String getId() { ... } 

Foo类有一个生成标识符的方法。

目前我只是手动调用setId(String id)方法,但希望有更好的方法来处理这种情况。

我不认为使用纯JPA-2 API使用自定义注释生成自定义ID是开箱即用的支持。 但是如果你想使用提供者特定的API,那么这项工作非常简单。 示例示例

要成为提供者独立尝试以下任何技巧….

IdGeneratorHolder

 public abstract class IdGeneratorHolder { /* PersistentEntity is a marker interface */ public static IdGenerator getIdGenerator(Class 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}; } } 

现在在no-arg构造函数@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 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(); } } 

UPDATE

如果我们将id生成放在no-arg构造函数中,那么从数据库加载实体时不会导致问题吗? 因为hibernate将调用no-arg构造函数,导致重新生成现有的id

是的,你是对的,我错过了那一部分。 :(实际上,我想告诉你: – 在我的应用程序中,每个Entity对象都与一个组织实体相关联;所以我创建了一个带有两个构造函数的抽象超类,并且每个实体(组织除外)都扩展了这个类。

  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方法(在类中声明的方法以及在超类中声明的所有其他方法)。 不能告诉你有什么问题,除非你展示一些代码和控制台。

您可以。

首先,实现org.hibernate.id.IdentifierGenerator

然后,您必须将其映射到映射xml文件中。 我找不到用注释做到这一点的方法:

     

最后,使用@GeneratedValue(generator="identifier-name")

请注意,这是特定于hibernate的(不是JPA)

更新:我看了一下Hibernate的来源,似乎在一个地方,在未能解析短名称后,hibernates尝试调用Class.forName(..) 。 那里的参数叫做strategy 。 所以这是你尝试的:

  • 尝试将类完全限定名称设置为generator属性中的字符串
  • 尝试将类fqn设置为@GenericGenerator strategy属性中的字符串(带有一些任意名称)

让我知道哪些(如果有的话)有效