我有一个表单要填写一个名为Father
的POJO。在里面,我有一个FotoFather
字段。
当我保存一个新的Father时,我会自动保存对象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;
...
}
如果您只是出于某些特定于应用程序的目的而需要完整的URL,我可能会错误地根本不尝试将URL与ID一起存储,而是依赖于一个临时方法。
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
s。
为了避免插入时发生冲突的问题,我们有一个序列服务类,它公开了对按名称获取下一个序列值的支持。序列表行在读取时被锁定,在提交时被更新。这可以防止多个会话在同一序列中出现并发问题,防止范围中的间隙,并允许提前了解标识符。
@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);
}
我认为您可以在Father类上注册@PostPersist
回调。正如JPA规范所指出的:
PostPersist和PostRemove回调方法用于在实体被持久化或删除之后,实体。这些回调也将在这些操作是级联的。PostPersist和PostRemove方法将在数据库插入和删除操作之后调用分别地这些数据库操作可以直接发生在已调用持久化、合并或删除操作,或者它们可能在冲洗操作发生后直接发生(可能在交易结束)生成的主键值为在PostPersist方法中可用
因此,回调应该在Father实例写入数据库之后和FotoFather实例编写之前立即调用。
public class Father(){
@PostPersist
public void updateFotoFather(){
fotofather.setNaturalUrl("/xyz/" + id);
}
}