如何在运行时知道POJO的ID(@GeneratedValue)

时间:2015-12-11 11:31:45

标签: java database hibernate spring-mvc orm

我有一个表单来填充名为Father的POJO。在里面,我有一个FotoFather字段。

当我保存一个新的父亲时,我会自动保存对象FotoFather(使用Hibernate ORM模式)。

FotoFather.fotoNaturalUrl必须填写Father.id的值,这就是问题!

当我在数据库中保存Father时,我当然还没有Father.id值来填充FotoFather.fotoNaturalUrl。我该如何解决这个问题?

谢谢

@Entity
@Table(name = "father")
public class Father implements Serializable{    
    ...
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    ...
    @OneToOne(targetEntity = FotoFather.class, fetch = FetchType.EAGER)
    @JoinColumn(name = "fotoFather", referencedColumnName = "id")
    @Cascade(CascadeType.ALL)
    private FotoFather fotoFather;
}

FotoFather.class

@Entity
@Table(name = "foto_father")
public class FotoFather.class{

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    ...
    @Column(name = "foto_natural_url")
    private String fotoNaturalUrl;
    ...
}

2 个答案:

答案 0 :(得分:1)

如果您只是需要完整的URL用于某些特定于应用程序的目的,我可能会错误地试图存储具有ID的URL,而是依赖于瞬态方法。

 public class FotoFather {
   @Transient
   public String getNaturalUrl() {
     if(fotoNaturalUrl != null && fotoNaturalUrl.trim().length > 0) {
       return String.format("%s?id=%d", fotoNaturalUrl, id);
     }
     return "";
   }
 }

事实上,将您的URL更多地分解为极简主义变量组件,并且只将它们存储在单独的列中可能会导致技术债务问题,特别是如果URL发生变化。这样,基本URL可以是应用程序可配置的,并且控制最终URL端点的变量方面都是您存储的。

但是如果您必须提前知道ID(或者在我最近的情况下,保持标识符顺序而不丢失单个值),则需要在持久化之前生成FotoFather标识符的方法实体,因此它们不是@GeneratedValue

为了避免插入时出现冲突问题,我们有一个序列服务类,它公开了对按名称提取下一个序列值的支持。序列表行在读取时被锁定并在提交时更新。这可以防止多个会话出现具有相同序列的并发问题,从而防止该范围中的间隙并允许提前知道标识符。

 @Transactional
 public void save(Father father) {
   Assert.isNotNull(father, "Father cannot be null.");
   Assert.isNotNull(father.getFotoFather(), "FotoFather cannot be null.");
   if(father.getFotoFather().getId() == null) {
     // joins existing transaction or errors if one doesn't exist
     // when sequenceService is invoked.
     Long id = sequenceService.getNextSequence("FOTOFATHER");
     // updates the fotofather's id
     father.getFotoFather().setId(id);        
   }
   // save.
   fatherRepository.save(father);
 }

答案 1 :(得分:1)

我认为你可以在你的父班上注册一个@PostPersist回调。正如JPA规范所指出的那样:

  

为一个调用PostPersist和PostRemove回调方法   实体被持久化或删除后的实体。这些   还将对所有这些实体调用回调   操作是级联的。 PostPersist和PostRemove方法将会   在数据库插入和删除操作之后调用   分别。这些数据库操作可能直接发生在   已经调用了持久化,合并或删除操作,或者可以调用它们   在刷新操作发生后直接发生(可能是在   交易结束)。 生成的主键值是   可以在PostPersist方法中使用。

因此,应该在将父实例写入数据库之后和写入FotoFather实例之前立即调用回调。

public class Father(){

    @PostPersist
    public void updateFotoFather(){
        fotofather.setNaturalUrl("/xyz/" + id);
    }
}