Java 为什么JPA双重插入子实体?
我有一对实体,称它们为“Ticket”和“TicketComment” 票证具有以下属性:Java 为什么JPA双重插入子实体?,java,jpa,spring-data-jpa,Java,Jpa,Spring Data Jpa,我有一对实体,称它们为“Ticket”和“TicketComment” 票证具有以下属性: @OneToMany(mappedBy="ticket", cascade = CascadeType.ALL) @OrderBy("date") private List<TicketComment> comments = new ArrayList<>(); 我认为,我正在使用的代码应该添加一条新注释。但是,它似乎向数据库添加了两次新注释: Ticket ticket = t
@OneToMany(mappedBy="ticket", cascade = CascadeType.ALL)
@OrderBy("date")
private List<TicketComment> comments = new ArrayList<>();
我认为,我正在使用的代码应该添加一条新注释。但是,它似乎向数据库添加了两次新注释:
Ticket ticket = ticketRepo.findOne(id);
TicketComment newComment = new TicketComment();
// ...
newComment.setTicket(ticket);
ticket.getComments().add(newComment);
ticketRepo.save(ticket);
我想我已经能够在没有重复的子实体的情况下成功地使用类似的实体了。。。我可能会错过什么
更新:一个解决办法似乎是:
ticketRepo
将任何其他更新保存到Ticket,而无需创建/添加注释newComment.setTicket(ticket);
),并将其保存为ticketCommentRepo
更新2:澄清观察到的症状 如果我试图简单地保存添加了新“TicketComment”的父“Ticket”实体,会发生什么情况?我看到在数据库中创建了两个不同的记录,它们具有不同的主键,但具有相同的注释文本和相同或几乎相同的时间戳 比如说,我贴了一条评论,像“看起来不错”。尽管我在调试器中确认在我的
ticket.getComments()
中只显示了一个“lookgood”。。。再次加载票证的详细信息页面后,我看到如下内容:
me@2015-09-11 23:16:58:看起来不错
me@2015-09-11 23:16:59:看起来不错
当我查看正在准备的SQL语句的一些调试日志时,我看到为我的TicketComments表调用了两个外观相同的INSERT语句,然后对我的Tickets表进行了一次更新…我真的不明白您所说的“将新注释添加到数据库两次”是什么意思。您是否看到两个注册中心具有相同的PK 如果您希望每个注释在票据集合中出现一次,我认为您应该使用
Set
界面,而不是List
。
无论如何,我将解释我对该代码片段的预期行为的了解
Ticket ticket = ticketRepo.findOne(id);
TicketComment newComment = new TicketComment();
罚单已经存在了?这并不重要,因为您将save()
it。然后创建一个新的注释
newComment.setTicket(ticket);
这是必要的,因为关联的所有者方位于TicketComment
实体中,并且保持对象模型的一致性(在某些情况下可能不需要)
在ticket.comments
collection上设置cascade=ALL时,在ticket
上触发的所有操作(保存、更新等)将传播到集合中的所有实体对象
ticketRepo.save(ticket);
当您将
ticket
传递到save()
方法时,操作将传播到您刚刚添加的newComment
实体。newComment
将被传递到saveOrUpdate()
,然后与ticket
的关系一起保存,您无需在newComment
中指定ticket
只需删除以下行
newComment.setTicket(ticket);
这将起作用,因为cascade
将自动将ticket
分配给newComment
,反之亦然
您实际上是双重分配的,这可能是重复的原因。您可以共享您的TickerPo findOne和save方法吗?这些方法是由Spring Data JPA生成的。之后是否保存newComment?是否使用Hibernate作为JPA提供程序?然后,这是的重复。你被冬眠虫击中了。链接帖子有多个解决方法,包括在将子实体添加到父实体之前保存子实体,使用
Set
而不是List
,等等。关联的所有者方位于TicketComment(@manytone)中,因此无法删除行以保持关系。级联只是传播保存操作。是的,我知道TicketComment
是所有者,但这也使它成为该关系中的子对象,因此当您坚持时,如果CascadeType,则父对象的子对象将被坚持。坚持
或CascadeType。所有
都适用于您所说的子对象“cascade将自动为新成员分配票证”,但cascade不会这样做,因为这是一个双向关联。删除该行违反JPA规范。从概念上讲,一个集合对我来说没有多大意义……首先,决定equals()的实现
很奇怪……从技术上讲,有人/可以/发布两次相同的注释。我让Spring数据JPA为我填充注释时间。另外,这些注释有一个逻辑顺序要应用(注释产生的时间),JPA可以为集合这样做吗?(如中所述,它是否支持使用a而不是常规?如果我理解你所说的。为什么设置有意义?首先是因为它是@manish评论的bug()的一个解决方法(你正在承受它)。其次,由于您表示与TicketComment
表中的字段/列的关联,因此每个TicketComment
可以属于一个Ticket.comments
集合,并且只在其中显示一次(类似于一个集合).List在概念上允许重复对象的外观,但不能用表示法保存这种状态。要清楚,每行TicketComment
都是唯一的注释(有一个id…需要明确的是,表的每一行都是一个唯一的注释。它有唯一的主键来标识它,不管另一行是否有相同的注释文本,它们是不同的。您知道实际情况和适用的内容,但相同的文本并不意味着它是相同的注释。例如,请注意stackoverflow对我的下一个c的作用omment…需要明确的是,表的每一行都是唯一的注释。它有唯一的主键来标识它,
ticketRepo.save(ticket);
newComment.setTicket(ticket);