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

问题描述:

我正在迁移旧系统以使用Hibernate 3.它当前会生成自己的标识符。为了保持系统当前的功能,然后尝试将其移到更好的位置,我将如何指定(使用注释)我自己的类,以便在插入时返回自定义生成的标识符?将自定义标识符分配给@id属性

是这样的:

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

Foo类具有生成所述标识符的一种方法。

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

我不认为这是用于使用定制标注使用纯JPA的2 API定制标识出的现成的支持。但是如果你想使用提供商特定的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或者设置为无参数构造函数,使用@PrePersist方法

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的。如果持久实体有不同类型的IDS .....

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注解的方法1倍以上,“唯一的ID将被改变!”所以在no-arg构造函数中设置id是可取的。或者为了解决这个问题,把一个非空检查...

@PrePersist 
    public void generateId() { 
     if(id != null) 
      id = IdGeneratorHolder.getIdGenerator(getClass()).generate(); 
    } 
} 

UPDATE

如果我们把ID生成的 无参数的构造函数,那不是 从数据库加载实体 时产生问题?因为Hibernate 将调用无参数的构造 导致现有的IDS是 重新生成

是的,你说得对,我错过了一部分。 :(其实,我想告诉你的是: - 在我的应用程序中每个实体对象与组织实体相关联的,所以我创建了一个抽象的超类有两个构造函数,每一个实体(组织除外)扩展该类

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

您可以。

一是落实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的来源,看起来在一个地方,在解决短名称失败后,休眠尝试呼叫Class.forName(..)。那里的参数叫做strategy。因此,这里是你尝试什么:

  • 尝试在generator属性
  • 尝试在@GenericGeneratorstrategy属性设置类FQN为字符串(有一些任意名称)
设置类完全合格的名称作为字符串

让我知道(如果有的话)的工作

+0

我看过一样,如果可能的话,想避免xml文件。但是如果我找不到替代品,我想我会诉诸于此。谢谢! – digiarnie 2010-11-24 01:47:09

+0

@digiarnie看到更新 – Bozho 2010-11-24 07:27:00