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

时间:2010-11-24 01:04:35

标签: java hibernate jpa

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

类似的东西:

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

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

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

2 个答案:

答案 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属性
  • 中的字符串
  • 尝试将类fqn设置为@GenericGenerator strategy属性中的字符串(带有一些任意名称)

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