将自定义标识符分配给@id属性 [英] Assign custom identifier to an @id property

查看:95
本文介绍了将自定义标识符分配给@id属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



类似于:

  @Id 
@CustomIdGenerator(Foo.class )//显然这不是一个真正的注释
public String getId(){...}

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



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

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



独立于提供商尝试以下任何技巧....



IdGeneratorHolder

$公共抽象类IdGeneratorHolder {
/ * * PersistentEntity是标记接口* /
public static IdGenerator getIdGenerator(Class <?extends PersistentEntity> entityType){
/ * sample imlelementation * /
if(Product.class.isAssignableFrom(entityType)){
return new ProductIdGenerator();

}
返回null;
}
}

常规IdGenerator界面

  public interface IdGenerator {
String generate();

特定IdGenerator - 产品ID生成器

  public class ProductIdGenerator实现IdGenerator {
public String generate(){
/ *一些复杂的逻辑转到这里* /
返回$ {generatedId};




$ b $ p
$ b现在将生成的id设置为no -arg构造函数或 in @PrePersist方法



Product.java

  public class Product implements PersistentEntity {

private String id;
$ b $ 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())。生成()toLong();
}

您也可以使用自定义注释...



CustomIdGenerator.java

  public @interface CustomIdGenerator {
IdStrategy strategy();
}

IdStrategy.java

 枚举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();
返回新的ProductIdGenerator(策略);
}

还有一件事情....如果我们在@PrePersist方法中设置了id, equals()方法不能依赖id字段(即代理键),我们必须使用业务/自然键来实现equals()方法。但是,如果我们在无参数构造函数中将id字段设置为某个唯一值(uuid或应用程序中唯一的app-uid),它可以帮助我们实现equals()方法。

  public boolean equals(Object obj){
if(obj instanceof Product){
Product that =(Product)obj;
返回this.id == that.id;
}
返回false;



$ b如果我们或其他人打电话(故意或错误)@PrePersist注释方法不止一次,唯一的ID将被改变!!!所以在no-arg构造函数中设置id是可取的。或者为了解决这个问题,把一个非空检查... ...

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




$ h $ UPDATE


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

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

 受保护PersistentEntityImpl(){
}

受保护PersistentEntityImpl(组织机构){
String entityId = UUIDGenerator.generate();
String organizationId = organization.getEntityId();
identifier = new EntityIdentifier(entityId,organizationId);
}

无参数构造函数用于JPA提供程序,我们从不调用无参数构造函数,而是基于其他组织构造函数。请参阅.id是在基于组织的构造函数中分配的(我在写回答时真的错过了这一点,对此抱歉)。

查看您是否可以实现这个或类似的策略在您的应用程序中。


第二个选项是使用
@PrePersist注释。我把它放在
中,并且这个方法从来没有被打到,给了
我一个例外,说明我需要
手动设置id。
还有什么我应该做的吗?

理想情况下,JPA提供者应该调用@PrePersist方法(在类中声明的方法以及在超类中声明的所有其他方法)之前持久化实体对象。不能告诉你什么是错的,除非你显示一些代码和控制台。


I'm migrating a legacy system over to use Hibernate 3. It currently generates its own identifiers. To keep with what the system currently does before I try and move it over to something a little better, how would I go about specifying (using annotations) my own class that will return the custom generated identifiers when an insert occurs?

Something like:

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

Where the Foo class has one method that generates the identifier.

Currently I'm just calling the setId(String id) method manually but was hoping for a better way to deal with this situation.

解决方案

I don't think there is out-of-box support for generating custom Ids using custom annotations using pure JPA-2 API. But if you want to use provider specific API, then the job is pretty simple. Sample Example

To be provider independent try any of following tricks....

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;
    }
}

General IdGenerator interface

public interface IdGenerator {
    String generate();
}

Specific IdGenerator - Product Id Generator

public class ProductIdGenerator implements IdGenerator {
    public String generate() {
            /* some complicated logic goes here */
        return ${generatedId};
    }
}

Now set the generated id either in no-arg constructor OR 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();
    }

}

In above example all the ids are of the same type i.e. java.lang.String. If the persistent entities have ids of different types.....

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();
    }

You can also use your custom annotation...

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);
        }

One more thing.... If we set id in @PrePersist method, the equals() method cannot rely on id field (i.e. surrogate key), we have to use business/natural key to implement equals() method. But if we set id field to some unique value (uuid or "app-uid" unique within application) in no-arg constructor, it helps us to implement the equals() method.

public boolean equals(Object obj) {
        if(obj instanceof Product) {
            Product that = (Product) obj;
            return this.id ==that.id;
        }
        return false;
    }

If we or someone else call (intentionally or by mistake) the @PrePersist annotated method more than one times, the "unique id will be changed!!!" So setting id in no-arg constructor is preferable. OR to address this issue put a not null check...

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

UPDATE

If we put the id generation in a no-arg constructor, wouldn't that cause a problem when loading entities from the database? because hibernate will call the no-arg constructor causing existing ids to be re-generated

Yeah you are right, I missed that part. :( Actually, I wanted to tell you that:- in my application every Entity object is associated with an Organization Entity; so I've created an abstract super class with two constructors, and every Entity (except Organization) extends this class.

    protected PersistentEntityImpl() {
    }

    protected PersistentEntityImpl(Organization organization) {
        String entityId = UUIDGenerator.generate();
        String organizationId = organization.getEntityId();
        identifier = new EntityIdentifier(entityId, organizationId);
    }

The no-arg constructor is for JPA provider, we never invoke no-arg constructor, but the other organization based constructor. As you can see. id is assigned in Organization based constructor. (I really missed this point while writing the answer, sorry for that).

See if you can implement this or similar strategy in your application.

The second option was using the @PrePersist annotation. I put that in and the method never got hit and gave me an exception stating that I needed to set the id manually. Is there something else I should be doing?

Ideally, JPA provider should invoke @PrePersist methods (one declared in class and also all the other methods that are declared in super-classes) before persisting the entity object. Can't tell you what is wrong, unless you show some code and console.

这篇关于将自定义标识符分配给@id属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆