【问题标题】:Assign custom identifier to an @id property将自定义标识符分配给 @id 属性
【发布时间】:2010-11-24 01:04:35
【问题描述】:

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

类似:

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

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

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

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

    我认为使用纯 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};
        }
    }
    

    现在在无参数构造函数@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<? 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 注解的方法,“唯一 id 将被更改!!!”所以在无参数构造函数中设置 id 是可取的。或者要解决这个问题,请进行非空检查...

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

    更新

    如果我们将 id 生成放在一个 没有参数的构造函数,不是吗 加载实体时导致问题 从数据库?因为冬眠 将调用无参数构造函数 导致现有的 id 重新生成

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

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

    【讨论】:

    • 感谢您的回复!我对你的两个建议有点困惑。如果我们将 id 生成放在一个无参数的构造函数中,那在从数据库加载实体时不会导致问题吗?因为休眠将调用无参数构造函数,导致重新生成现有的 id,这不是我想要的(即使我签入了空值)。第二个选项是使用 @PrePersist 注释。我把它放进去,这个方法从来没有被击中,并给了我一个异常,说明我需要手动设置 id。还有什么我应该做的吗?
    • @digiarnie +1 指出错误。检查答案中的更新部分。
    【解决方案2】:

    你可以。

    首先,实现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 的来源,似乎在一个地方,在解析短名称失败后,hibernate 尝试调用Class.forName(..)。那里的参数称为strategy。因此,您可以尝试以下方法:

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

    让我知道哪个(如果有)有效

    【讨论】:

    • 我也看到了同样的情况,如果可能的话,我想避免使用 xml 文件。但我想如果我找不到替代方案,我会求助于这个。谢谢!
    • 我在 id 字段中添加了以下注释:@GeneratedValue(generator = "spike.TestIdGenerator") 导致启动时出现以下错误:org.hibernate.AnnotationException: Unknown Id.generator: peak .TestIdGenerator - 然后我尝试了这个注释:@GenericGenerator(name = "foo", strategy = "spike.TestIdGenerator") 尝试保存时导致此异常:org.hibernate.id.IdentifierGenerationException: ids for this class must在调用 save() 之前手动分配
    猜你喜欢
    • 2019-11-24
    • 1970-01-01
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 1970-01-01
    • 2017-12-14
    • 1970-01-01
    相关资源
    最近更新 更多