【问题标题】:Spring Boot map OneToMany realionship request value to controller modelSpring Boot 将 OneToMany realionship 请求值映射到控制器模型
【发布时间】:2021-01-30 18:57:14
【问题描述】:

好的,所以我开始学习 Spring Boot,但我正在努力处理客户端上的模型值映射。

标准字段似乎可以正常工作,但是当我有一个表示两个表之间关系的字段时,系统似乎无法自动识别所选值,尽管它看起来很直观,但它应该能够做到。

所以,我有两个反映银行和客户的模型类,其中每个客户都有一个关联的银行:

这是客户端类

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;
    }
    
}

这是银行类

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 个错误 字段“banco”上的对象“clientes”中的字段错误:拒绝值 [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] 转换为类型 [java.lang.Integer] 的值 'databaseModel.Banco@5adaf23d';嵌套异常是 java.lang.NumberFormatException: For input string: "databaseModel.Banco@5adaf23d"]

据我了解...框架无法将存储在银行选择中的值转换为银行类类型。我想知道......如果一切都正确连接,引擎是否应该能够正确猜测哪些数据必须加载到新的客户端对象 banco 属性中?

【问题讨论】:

    标签: java spring spring-boot model-view-controller autowired


    【解决方案1】:

    你是对的:&lt;option ... /&gt; 标记包含Banco 对象的字符串表示,即类似于databaseModel.Banco@212313221。它不能绑定到Banco 对象。

    最简单的解决方案是只保留banco.idth:fieldth:value 属性):

    <select th:field="*{banco.id}">
        <option th:each="banco: ${bancos}" th:value="${banco.id}" th:text=${banco.nombre} /> 
    </select>
    

    它会起作用,但你会在控制器中得到一个不完整的Banco:只会设置id。您需要手动执行数据库查找。

    另一种选择是创建自定义类型转换器。 &lt;select&gt; 标签会将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));
        }
    }
    

    【讨论】:

    • 好的,我解决了这个问题,在 Clientes.java 中添加了一个 getter 和一个 setter 来管理 id_banco 属性并在表单中使用 banco.id。对我来说,这似乎比使用转换器更优雅。有趣的事实是,我完全相信 Hibernate 引擎能够开箱即用地做到这一点,因为我通过注释告诉了它所需的所有信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 2019-11-25
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    相关资源
    最近更新 更多