我有一个表单来填充名为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;
...
}
答案 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);
}
}