spring-boot - SpringBoot CascadeType ALL vs MERGE 和分离实体
问题描述
我有以下实体:
@Entity
@Getter @Setter @NoArgsConstructor @RequiredArgsConstructor
public class Link extends Auditable {
@Id
@GeneratedValue
private Long id;
@NonNull
private String title;
@NonNull
private String url;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "link")
private List<Comment> comments = new ArrayList<>();
@Transient
@Setter(AccessLevel.NONE)
private String userAlias ;
public String getUserAlias() {
if(user == null)
return "";
return user.getAlias();
}
@ManyToOne
private User user;
public Long getUser() {
if(user == null)
return -1L;
return user.getId();
}
public void addComment(Comment c) {
comments.add(c);
c.setLink(this);
}
}
@Entity
@Getter @Setter @NoArgsConstructor @RequiredArgsConstructor
public class Comment extends Auditable{
@Id
@GeneratedValue
private Long id;
@NonNull
private String comment;
@ManyToOne(fetch = FetchType.LAZY)
private Link link;
public Long getLink() {
return link.getId();
}
}
如果我创建评论和链接,请将链接与评论相关联,然后保存。
例如:
Link link = new Link("Getting started", "url");
Comment c = new Comment("Hello!");
link.addComment(c);
linkRepository.save(link);
但是,如果我先保存评论:
Link link = new Link("Getting started", "url");
Comment c = new Comment("Hello!");
commentRepository.save(c);
link.addComment(c);
linkRepository.save(link);
我明白了
Caused by: org.hibernate.AnnotationException: @OneToOne or @ManyToOne on uk.me.dariosdesk.dariodemo.domain.Comment.link references an unknown entity: uk.me.dariosdesk.dariodemo.domain.Link
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:97) ~[hibernate-core-5.4.0.Final.jar:5.4.0.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1815) ~[hibernate-core-5.4.0.Final.jar:5.4.0.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1759) ~[hibernate-core-5.4.0.Final.jar:5.4.0.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1646) ~[hibernate-core-5.4.0.Final.jar:5.4.0.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287) ~[hibernate-core-5.4.0.Final.jar:5.4.0.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:903) ~[hibernate-core-5.4.0.Final.jar:5.4.0.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:934) ~[hibernate-core-5.4.0.Final.jar:5.4.0.Final]
将级联类型从 ALL 更改为 MERGE 似乎可以解决问题并接受这两种实现。(即:添加预先存在的评论或创建两者然后通过链接保存)
1)这是为什么?2)在使用 MERGE 而不是 ALL 时我应该注意什么?
解决方案
存储库保存方法检查实体是否存在。对于新实体,持久化被调用,对于持久化实体,合并被调用。
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
在第二个用例中,链接是新实体,因此调用了 persist()。CascadeType.ALL persist() 级联到 Comment 实体。评论已经持久化,需要合并,persist() 失败。
如果您使用 CascadeType.MERGE,则 persist() 不会级联到 Comment。它不会失败。
推荐阅读
- javascript - 如何从 json 文件中延迟加载对象及其属性
- nat - Kamailio 5.0.* 如果我知道 UAC 在一个或多个 NAT 之后,我应该如何获取私有 IP 地址?
- angular - 添加数据后如何用新数据加载ng2-smart-table?
- python - 在存储队列触发的 Azure 函数中重新排队消息 - 不引发异常
- python - Python Selenium 无法在linkedin.com 上找到 Element,Chrome 开发者控制台可以找到它
- python - 在 python 中附加多个 CSV 文件并使用文件名创建一个新列
- c# - DbContext 中的事件处理程序
- parsing - 解析器测试方法和工具
- php - 多对多关系的原则约束
- javascript - 如何更新状态数组?