春季 - 从网络表单更新一对多集合



我正在尝试删除表单中的一个值。

public class Parameter {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "param_id")
private Long id;
@Column(name = "param_name")
@NotEmpty
private String name;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "param_id", referencedColumnName = "param_id")
private List<ParamValue> values;
}

public class ParamValue {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "value_id")
private Long id;
@Column(name = "value_name")
private String name;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="param_id")
private Parameter parameter;
}

形式:

<form:form method="post" modelAttribute="parameter">
<form:input path="name" />
<c:forEach items="${parameter.values}" var="paramValue" varStatus="uStatus">
<div>
<form:input path="values[${uStatus.index}].id" type="hidden"/>
<form:input path="values[${uStatus.index}].name" type="text" />
<form:input path="values[${uStatus.index}].parameter" type="hidden" />
<button type="button" onclick="$(this).parent().remove();">x</button>
</div>
</c:forEach>
<input type="submit" value="save" class="btn btn-success">
</form:form>

在我的数据库中存在下一个值:

在帕拉姆:

------------------
| id | name      |
------------------
| 3  | TestParam |
------------------

在参数值中:

-----------------------------
| id  | name    | parameter |
-----------------------------
| 116 | value 1 | 3         |
-----------------------------
| 117 | value 2 | 3         |
-----------------------------
| 119 | value 3 | 3         |
-----------------------------
| 152 | value 4 | 3         |
-----------------------------

和控制器:

@Controller(value = "ParamsSave")
@RequestMapping("/params/save")
public class Save extends Base {
@Autowired
private ParamRepo paramRepo;
@ModelAttribute("parameter")
public Parameter getParam(@RequestParam(value = "id", required = false) Long param_id) {
if(param_id == null) {
return new Parameter();
}
Parameter parameter = paramRepo.findById(param_id);
return parameter == null ? new Parameter() : parameter;
}
@GetMapping
public String get(Parameter parameter) {
return getTemplate();
}
@PostMapping
public String post(@Valid Parameter parameter, BindingResult result) {
if(result.hasErrors()) {
return getTemplate();
}
paramRepo.save(parameter);
return "redirect: /";
}
}

如果我删除值"值 2",则会出现错误:

Bean 实例的标识符。参数值从 117 更改为 119;嵌套异常是 org.hibernate.HibernateException: identifier 的豆类实例。参数值从 117 更改为 119

如果我删除最后一个值,则没有任何反应,没有错误,字段不删除。

为什么会发生这种情况,如何从列表中删除值?

我可以在你的代码中看到三个问题:

第一个是关联映射错误。在双向关联中,一侧必须是拥有侧,另一侧是反向侧。正确的映射如下:

@OneToMany(mappedBy = "parameter", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ParamValue> values;

@ManyToOne
@JoinColumn(name="param_id")
private Parameter parameter;

请注意,我还删除了cascade=ALL,因为它在@ManyToOne的上下文中毫无意义(例如,您不能将ParameterValue的删除级联到其拥有Parameter,因为其他ParameterValue仍然会引用它,从而导致约束冲突异常)。

话虽如此,我严重怀疑是否需要双向关联。为什么不简单地将Parameter.values映射为:

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "param_id", referencedColumnName = "param_id")
private List<ParamValue> values;

并完全放弃ParamValue.parameter字段?从您描述的用例来看,无论如何ParamValues都是Parameter私有

另一个问题是 HTML 表单输入只能与简单值一起使用。我不完全确定以下行的作用:

<form:input path="values[${uStatus.index}].parameter" type="hidden" />

但是,它可能不会按照您的期望进行操作。无论如何,我都不会依赖在Save.post处理程序中正确设置ParameterValue.parameter字段。如果选择保留ParameterValue.parameter字段,则应循环访问Parameter.values列表,并为每个元素手动设置字段。

最后,使用$(this).parent().remove()意味着提交的parameter.values[]数组中将存在间隙(即原本对应于已删除元素的索引将丢失),这与你观察到的删除最后一个值的行为不同是一致的。您绝对应该使用调试器检查Save.post收到的Parameter.values内容。

最新更新