保存新实体时在 ManyToOne/OneToMany 关系上创建的重复条目
Duplicate entries created on ManyToOne/OneToMany relationship when saving a new entity
运行 Spring 4.1.4 / Hibernate Entity Manager 4.1.8 / Spring JPA 2.1 on Tomcat 8 with MySQL 5.6.
我有两个实体。我们称它们为 Person 和 Widget。
人class:
@Entity(name = "person")
public class Person {
@Id
@GeneratedValue
private Long id;
//... Other properties omitted
@OneToMany(fetch = FetchType.LAZY, mappedBy = "person", cascade = {CascadeType.ALL})
private Set<Widget> widgets = new HashSet<>();
public Set<Widget> getWidgets() {
return widgets;
}
public void setWidgets(Set<Widget> widgets) {
this.widgets = widgets;
}
public boolean addWidget(Widget widget) {
return this.widgets.add(widget);
}
//...other getters/setters omitted
}
@Entity(name = "widget")
public class Widget {
@Id
@GeneratedValue()
private Long id;
@Column(name = "part_id")
private String partId;
//... Other properties omitted
@ManyToOne
@JoinColumn(name = "person_id")
private Person person;
//...getters/setters omitted
}
我有两个数据库 table 设置如下:
CREATE TABLE `widget` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`person_id` bigint(20) NOT NULL,
`active` tinyint(4) DEFAULT '1',
`status_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `widgets_ix1` (`part_id`,`active`),
--some properties omitted
KEY `widgets_fk1` (`person_id`),
CONSTRAINT `person_fk1` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE
) --Some irrelevant details omitted
CREATE TABLE `person` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
--some properties omitted
`status_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
`create_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`)
)
现在开始讨论这个问题。
我有一个调用@Transactional 服务的restlet 调用。该服务创建一个新的 Widget。设置一些属性并尝试通过 addWidget(...) 将其添加到 Person。然后我调用另一个只执行 saveAndFlush() 的服务方法。
当服务 returns 到 restlet 时,会发生提交,但这会触发向小部件 table 的第二次插入。 (它对 part_id + 活动字段有唯一约束)。我一生都无法弄清楚为什么会这样。我已经尝试了几乎所有可能的保存和刷新人、小部件的组合,两者都是,先刷新,后刷新,默认属性。我想我现在很困惑,以至于我无法直视这个问题的核心。
事务在服务方法中开始,在服务方法结束时结束。 (唯一的传播是默认的 REQUIRED)。无需担心多重访问,因此并发不是问题。
下面是调用服务方法的逻辑。
@Override
@Transactional
public Message updateWidgetAdd(WidgetUpdateRequest message) {
//Validation
Person person = personService.findById(message.getPersonId());
try {
Widget widget = new Widget();
widget.setPartId(message.getPartId().toLowerCase());
widget.setActive(true);
widget.setPerson(person);
person.addWidget(widget);
personService.save(person);
return createSuccessPayload("id", widget.getId(), "partId", widget.getPartId());
} catch(Exception e) {
return createFailure("Uh oh.");
}
}
我完全被难住了。欢迎任何advice/help!
看起来像 bug in hibernate。您可以尝试先保存 child,然后再保存 parent 和 child 关系。
我想我可以关闭这个问题了。我发现了问题。
注意:在代码中查找问题时,确保问题不是简单的;例如不小心连续快速调用了两个 restlet 调用,因为你不小心调用了 ajax javascript 函数(添加到按钮)两次。
运行 Spring 4.1.4 / Hibernate Entity Manager 4.1.8 / Spring JPA 2.1 on Tomcat 8 with MySQL 5.6.
我有两个实体。我们称它们为 Person 和 Widget。
人class:
@Entity(name = "person")
public class Person {
@Id
@GeneratedValue
private Long id;
//... Other properties omitted
@OneToMany(fetch = FetchType.LAZY, mappedBy = "person", cascade = {CascadeType.ALL})
private Set<Widget> widgets = new HashSet<>();
public Set<Widget> getWidgets() {
return widgets;
}
public void setWidgets(Set<Widget> widgets) {
this.widgets = widgets;
}
public boolean addWidget(Widget widget) {
return this.widgets.add(widget);
}
//...other getters/setters omitted
}
@Entity(name = "widget")
public class Widget {
@Id
@GeneratedValue()
private Long id;
@Column(name = "part_id")
private String partId;
//... Other properties omitted
@ManyToOne
@JoinColumn(name = "person_id")
private Person person;
//...getters/setters omitted
}
我有两个数据库 table 设置如下:
CREATE TABLE `widget` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`person_id` bigint(20) NOT NULL,
`active` tinyint(4) DEFAULT '1',
`status_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `widgets_ix1` (`part_id`,`active`),
--some properties omitted
KEY `widgets_fk1` (`person_id`),
CONSTRAINT `person_fk1` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE
) --Some irrelevant details omitted
CREATE TABLE `person` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
--some properties omitted
`status_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
`create_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`)
)
现在开始讨论这个问题。
我有一个调用@Transactional 服务的restlet 调用。该服务创建一个新的 Widget。设置一些属性并尝试通过 addWidget(...) 将其添加到 Person。然后我调用另一个只执行 saveAndFlush() 的服务方法。
当服务 returns 到 restlet 时,会发生提交,但这会触发向小部件 table 的第二次插入。 (它对 part_id + 活动字段有唯一约束)。我一生都无法弄清楚为什么会这样。我已经尝试了几乎所有可能的保存和刷新人、小部件的组合,两者都是,先刷新,后刷新,默认属性。我想我现在很困惑,以至于我无法直视这个问题的核心。
事务在服务方法中开始,在服务方法结束时结束。 (唯一的传播是默认的 REQUIRED)。无需担心多重访问,因此并发不是问题。
下面是调用服务方法的逻辑。
@Override
@Transactional
public Message updateWidgetAdd(WidgetUpdateRequest message) {
//Validation
Person person = personService.findById(message.getPersonId());
try {
Widget widget = new Widget();
widget.setPartId(message.getPartId().toLowerCase());
widget.setActive(true);
widget.setPerson(person);
person.addWidget(widget);
personService.save(person);
return createSuccessPayload("id", widget.getId(), "partId", widget.getPartId());
} catch(Exception e) {
return createFailure("Uh oh.");
}
}
我完全被难住了。欢迎任何advice/help!
看起来像 bug in hibernate。您可以尝试先保存 child,然后再保存 parent 和 child 关系。
我想我可以关闭这个问题了。我发现了问题。
注意:在代码中查找问题时,确保问题不是简单的;例如不小心连续快速调用了两个 restlet 调用,因为你不小心调用了 ajax javascript 函数(添加到按钮)两次。