Spring 将 OneToMany 关系请求值引导映射到控制器模型
Spring Boot map OneToMany realionship request value to controller model
好的,所以我开始学习 Spring Boot,我正在努力处理客户端上的模型值映射。
它似乎与标准字段一起工作,但是当我有一个表示两个表之间关系的字段时,系统似乎无法自动识别 selected 值,尽管它看起来很直观应该可以做到。
所以,我有两个模型 classes 反映银行和客户,其中每个客户都有一个关联的银行:
这是客户class
package databaseModel;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Entity
public class Clientes {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@ManyToOne
@JoinColumn(name = "id_banco")
private Banco banco;
private String nombre;
private String apellidos;
private String direccion;
private String documento;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Banco getBanco() {
return banco;
}
public void setBanco(Banco banco) {
this.banco = banco;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellidos() {
return apellidos;
}
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
public String getDireccion() {
return direccion;
}
public void setDireccion(String direccion) {
this.direccion = direccion;
}
public String getDocumento() {
return documento;
}
public void setDocumento(String documento) {
this.documento = documento;
}
}
这是银行 class
package databaseModel;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@Entity
public class Banco {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String cif;
private String bic;
private String direccion;
private String nombre;
@OneToMany(mappedBy = "banco")
private List<Clientes> clientes;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCif() {
return cif;
}
public void setCif(String cif) {
this.cif = cif;
}
public String getBic() {
return bic;
}
public void setBic(String bic) {
this.bic = bic;
}
public String getDireccion() {
return direccion;
}
public void setDireccion(String direccion) {
this.direccion = direccion;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
现在,我正在尝试创建一个可以添加新客户的页面。为此,我有两种控制器方法,一种用于管理表单呈现,另一种用于检索转换为客户端模型的表单数据并将其保存到数据库。
这些是我的控制器方法:
@Controller
public class Controladores {
@Autowired
private ClientesRepository clientes;
@Autowired
private BancoRepository bancos;
@GetMapping("/nuevoCliente")
public String nuevoCliente(Model model) {
Clientes nuevoCliente = new Clientes();
model.addAttribute("cliente", nuevoCliente);
model.addAttribute("bancos", bancos.findAll());
return "nuevoCliente";
}
@PostMapping("/nuevoCliente")
public String nuevoCliente(@ModelAttribute Clientes nuevoCliente, Model model) {
clientes.save(nuevoCliente);
model.addAttribute("clientes", clientes.findAll());
return "gestionClientes";
}
}
最后,我有一个呈现表单的视图,以便用户可以将新的客户端数据发送到服务器:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Gestión bancaria</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Alta de un nuevo usuario, por favor, introduce los datos del usuario y pulsa el botón guardar</p>
<form method="post" action="#" th:action="@{/nuevoCliente}" th:object="${cliente}">
<input type="text" th:field="*{nombre}" placeholder="Nombre" />
<input type="text" th:field="*{apellidos}" placeholder="Apellidos" />
<input type="text" th:field="*{documento}" placeholder="NIF" />
<input type="text" th:field="*{direccion}" placeholder="Dirección" />
<select th:field="*{banco}">
<option th:each="banco: ${bancos}" th:value="${banco}" th:text=${banco.nombre} />
</select>
<input type="submit" value="Guardar" />
</form>
</body>
</html>
现在,当我尝试发送表单时,出现异常:
出现意外错误(类型=错误请求,状态=400)。
对象='clientes' 的验证失败。错误计数:1
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 个错误
对象 'clientes' 字段 'banco' 中的字段错误:拒绝值 [databaseModel.Banco@5adaf23d];代码 [typeMismatch.clientes.banco,typeMismatch.banco,typeMismatch.databaseModel.Banco,typeMismatch];参数 [org.springframework.context.support.DefaultMessageSourceResolvable: 代码 [clientes.banco,banco];参数 [];默认消息[banco]];默认消息 [无法将类型 'java.lang.String' 的 属性 值转换为 属性 'banco' 所需的类型 'databaseModel.Banco';嵌套异常是 org.springframework.core.convert.ConversionFailedException:无法从类型 [java.lang.String] 转换为值 'databaseModel.Banco@5adaf23d' 的类型 [java.lang.Integer];嵌套异常是 java.lang.NumberFormatException:对于输入字符串:“databaseModel.Banco@5adaf23d”]
据我了解...框架无法将存储在银行 select 上的值转换为银行 class 类型。我想知道...如果一切都正确连线,引擎难道不应该能够正确猜测哪些数据必须加载到新的客户端对象 banco 属性中吗?
你是对的:<option ... />
标签包含 Banco
对象的字符串表示,即类似 databaseModel.Banco@212313221
的内容。它不能绑定到 Banco
对象。
最简单的解决方案是仅保留 banco.id
(th:field
和 th:value
属性):
<select th:field="*{banco.id}">
<option th:each="banco: ${bancos}" th:value="${banco.id}" th:text=${banco.nombre} />
</select>
它会工作,但你会在控制器中得到一个不完整的 Banco
:只会设置 id
。您需要手动执行数据库查找。
另一种选择是创建自定义类型转换器。 <select>
标记将发送 banco.id
作为 client.banco
的值,转换器将执行数据库查找。
<select th:field="*{banco}">
<option th:each="banco: ${bancos}" th:value="${banco.id}" th:text=${banco.nombre} />
</select>
@Component
public class BancoConverter implements Converter<String, Banco> {
@Autowired
private BancoRepository bancos;
@Override
public Banco convert(String id) {
return bancos.findById(Integer.parseInt(id));
}
}
好的,所以我开始学习 Spring Boot,我正在努力处理客户端上的模型值映射。
它似乎与标准字段一起工作,但是当我有一个表示两个表之间关系的字段时,系统似乎无法自动识别 selected 值,尽管它看起来很直观应该可以做到。
所以,我有两个模型 classes 反映银行和客户,其中每个客户都有一个关联的银行:
这是客户class
package databaseModel;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Entity
public class Clientes {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@ManyToOne
@JoinColumn(name = "id_banco")
private Banco banco;
private String nombre;
private String apellidos;
private String direccion;
private String documento;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Banco getBanco() {
return banco;
}
public void setBanco(Banco banco) {
this.banco = banco;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellidos() {
return apellidos;
}
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
public String getDireccion() {
return direccion;
}
public void setDireccion(String direccion) {
this.direccion = direccion;
}
public String getDocumento() {
return documento;
}
public void setDocumento(String documento) {
this.documento = documento;
}
}
这是银行 class
package databaseModel;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@Entity
public class Banco {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String cif;
private String bic;
private String direccion;
private String nombre;
@OneToMany(mappedBy = "banco")
private List<Clientes> clientes;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCif() {
return cif;
}
public void setCif(String cif) {
this.cif = cif;
}
public String getBic() {
return bic;
}
public void setBic(String bic) {
this.bic = bic;
}
public String getDireccion() {
return direccion;
}
public void setDireccion(String direccion) {
this.direccion = direccion;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
现在,我正在尝试创建一个可以添加新客户的页面。为此,我有两种控制器方法,一种用于管理表单呈现,另一种用于检索转换为客户端模型的表单数据并将其保存到数据库。
这些是我的控制器方法:
@Controller
public class Controladores {
@Autowired
private ClientesRepository clientes;
@Autowired
private BancoRepository bancos;
@GetMapping("/nuevoCliente")
public String nuevoCliente(Model model) {
Clientes nuevoCliente = new Clientes();
model.addAttribute("cliente", nuevoCliente);
model.addAttribute("bancos", bancos.findAll());
return "nuevoCliente";
}
@PostMapping("/nuevoCliente")
public String nuevoCliente(@ModelAttribute Clientes nuevoCliente, Model model) {
clientes.save(nuevoCliente);
model.addAttribute("clientes", clientes.findAll());
return "gestionClientes";
}
}
最后,我有一个呈现表单的视图,以便用户可以将新的客户端数据发送到服务器:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Gestión bancaria</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Alta de un nuevo usuario, por favor, introduce los datos del usuario y pulsa el botón guardar</p>
<form method="post" action="#" th:action="@{/nuevoCliente}" th:object="${cliente}">
<input type="text" th:field="*{nombre}" placeholder="Nombre" />
<input type="text" th:field="*{apellidos}" placeholder="Apellidos" />
<input type="text" th:field="*{documento}" placeholder="NIF" />
<input type="text" th:field="*{direccion}" placeholder="Dirección" />
<select th:field="*{banco}">
<option th:each="banco: ${bancos}" th:value="${banco}" th:text=${banco.nombre} />
</select>
<input type="submit" value="Guardar" />
</form>
</body>
</html>
现在,当我尝试发送表单时,出现异常:
出现意外错误(类型=错误请求,状态=400)。 对象='clientes' 的验证失败。错误计数:1 org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 个错误 对象 'clientes' 字段 'banco' 中的字段错误:拒绝值 [databaseModel.Banco@5adaf23d];代码 [typeMismatch.clientes.banco,typeMismatch.banco,typeMismatch.databaseModel.Banco,typeMismatch];参数 [org.springframework.context.support.DefaultMessageSourceResolvable: 代码 [clientes.banco,banco];参数 [];默认消息[banco]];默认消息 [无法将类型 'java.lang.String' 的 属性 值转换为 属性 'banco' 所需的类型 'databaseModel.Banco';嵌套异常是 org.springframework.core.convert.ConversionFailedException:无法从类型 [java.lang.String] 转换为值 'databaseModel.Banco@5adaf23d' 的类型 [java.lang.Integer];嵌套异常是 java.lang.NumberFormatException:对于输入字符串:“databaseModel.Banco@5adaf23d”]
据我了解...框架无法将存储在银行 select 上的值转换为银行 class 类型。我想知道...如果一切都正确连线,引擎难道不应该能够正确猜测哪些数据必须加载到新的客户端对象 banco 属性中吗?
你是对的:<option ... />
标签包含 Banco
对象的字符串表示,即类似 databaseModel.Banco@212313221
的内容。它不能绑定到 Banco
对象。
最简单的解决方案是仅保留 banco.id
(th:field
和 th:value
属性):
<select th:field="*{banco.id}">
<option th:each="banco: ${bancos}" th:value="${banco.id}" th:text=${banco.nombre} />
</select>
它会工作,但你会在控制器中得到一个不完整的 Banco
:只会设置 id
。您需要手动执行数据库查找。
另一种选择是创建自定义类型转换器。 <select>
标记将发送 banco.id
作为 client.banco
的值,转换器将执行数据库查找。
<select th:field="*{banco}">
<option th:each="banco: ${bancos}" th:value="${banco.id}" th:text=${banco.nombre} />
</select>
@Component
public class BancoConverter implements Converter<String, Banco> {
@Autowired
private BancoRepository bancos;
@Override
public Banco convert(String id) {
return bancos.findById(Integer.parseInt(id));
}
}