You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot类型转换异常:String转Address/Long失败求助

问题描述

开发Spring Boot地址管理模块时,使用Thymeleaf表单提交数据触发400 Bad Request错误,核心报错信息:

无法将java.lang.String类型的值转换为所需的example.entity.Address类型;无法将值'madhapur'从java.lang.String类型转换为java.lang.Long类型

对应异常为org.springframework.beans.TypeMismatchException,完整栈跟踪:

at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:79)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:53)
at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:729)
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttributeFromRequestValue(ServletModelAttributeMethodProcessor.java:142)
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:78)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:147)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:181)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:148)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)

相关代码

Address实体类

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name="addresses")
public class Address {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long add_id;

    @Column
    private String address;

    @Column
    private String postalcode;

    @Column
    private String city;

    @Column
    private String state;

    @Column
    private String country;

    // 手动编写的getter/setter(可删除,因已使用@Getter@Setter)
    public Long getAdd_id() { return add_id; }
    public void setAdd_id(Long add_id) { this.add_id = add_id; }
    public String getAddress() { return address; }
    public void setAddress(String address) { this.address = address; }
    public String getPostalcode() { return postalcode; }
    public void setPostalcode(String postalcode) { this.postalcode = postalcode; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public String getState() { return state; }
    public void setState(String state) { this.state = state; }
    public String getCountry() { return country; }
    public void setCountry(String country) { this.country = country; }
}

Thymeleaf表单

<form
    method="post"
    role="form"
    th:action="@{/address/save}"
    th:object="${address}"
>
    <input type="hidden" th:field="*{add_id}" /> 

    <div class="form-group mb-3">
        <label class="form-label">Address</label>
        <input
                class="form-control"
                id="address"
                name="address"
                placeholder="Enter Address"
                th:field="*{address}"
                type="text"
        />
        <p th:errors = "*{address}" class="text-danger"
        th:if="${#fields.hasErrors('address')}"></p>
    </div>
    
    <div class="form-group mb-3">
        <label class="form-label">Postal Code</label>
        <input
                class="form-control"
                id="postalcode"
                name="postalcode"
                placeholder="Enter Postal Code"
                th:field="*{postalcode}"
                type="text"
        />
        <p th:errors = "*{postalcode}" class="text-danger"
           th:if="${#fields.hasErrors('postalcode')}"></p>
    </div>
    
    <div class="form-group mb-3">
        <label class="form-label">City</label>
        <input
                class="form-control"
                id="city"
                name="city"
                placeholder="Enter City"
                th:field="*{city}"
                type="text"
        />
        <p th:errors = "*{city}" class="text-danger"
           th:if="${#fields.hasErrors('city')}"></p>
    </div>
    
    <div class="form-group mb-3">
        <label class="form-label">State</label>
        <input
                class="form-control"
                id="state"
                name="state"
                placeholder="Enter State"
                th:field="*{state}"
                type="text"
        />
        <p th:errors = "*{state}" class="text-danger"
           th:if="${#fields.hasErrors('state')}"></p>
    </div>
    
    <div class="form-group mb-3">
        <label class="form-label">Country</label>
        <input
                class="form-control"
                id="country"
                name="country"
                placeholder="Enter Country"
                th:field="*{country}"
                type="text"
        />
        <p th:errors = "*{country}" class="text-danger"
           th:if="${#fields.hasErrors('country')}"></p>
    </div>
    
    <div class="form-group" style="text-align:center">
        <button class="btn btn-success" type="submit">Save</button>
    </div>
</form>

Controller代码

@Autowired
private AddressRepository addressRepository;

@GetMapping("/address/{id}")
public String addAddress(@PathVariable("id") Long add_id, Model model){
    System.out.println("\nInside addressform :\n");
    @SuppressWarnings("deprecation")
    Address address = addressRepository.getById(add_id);
    System.out.println(address.getAdd_id());
    System.out.println("add_id : "+add_id);
    model.addAttribute("address", address);
    return "addressform";
}

// handler method to handle post request to save address
@PostMapping("/address/save")
public String saveAddress(@Valid @ModelAttribute("address") Address address) {
    System.out.println("\nInside address save method.");
    System.out.println(address.getAdd_id());
    System.out.println(address.getAddress());
    System.out.println(address.getPostalcode());
    System.out.println(address.toString());
    addressRepository.save(address);
    return "redirect:/users";
}

问题原因

  1. 模型属性名与实体字段名冲突:Controller中@ModelAttribute("address")的名称与Address实体类中的address字段名完全一致,导致Spring MVC参数绑定逻辑混乱。
  2. 表单提交时,名为address的请求参数(对应实体的地址字符串值)被Spring错误识别为用于初始化Address类型模型对象的参数,进而尝试将字符串转换为Address实例,触发类型转换异常。
  3. 混乱的绑定逻辑同时导致地址字符串(如'madhapur')被错误绑定到add_id(Long类型)字段,引发第二次类型转换错误。
  4. 额外问题:实体类已使用@Getter@Setter注解,手动编写的getter/setter属于冗余代码;表单中手动添加的name属性可删除,th:field会自动生成正确的属性值。

解决方案

1. 修改模型属性名称,避免冲突

  • GET请求方法:将model.addAttribute("address", address)改为model.addAttribute("addr", address)
  • POST请求方法:将@ModelAttribute("address")改为@ModelAttribute("addr")
  • Thymeleaf表单:将th:object="${address}"改为th:object="${addr}",其余th:field表达式保持不变

修改后的Controller代码片段:

@GetMapping("/address/{id}")
public String addAddress(@PathVariable("id") Long add_id, Model model){
    System.out.println("\nInside addressform :\n");
    @SuppressWarnings("deprecation")
    Address address = addressRepository.getById(add_id);
    System.out.println(address.getAdd_id());
    System.out.println("add_id : "+add_id);
    model.addAttribute("addr", address); // 修改此处
    return "addressform";
}

@PostMapping("/address/save")
public String saveAddress(@Valid @ModelAttribute("addr") Address address) { // 修改此处
    System.out.println("\nInside address save method.");
    System.out.println(address.getAdd_id());
    System.out.println(address.getAddress());
    System.out.println(address.getPostalcode());
    System.out.println(address.toString());
    addressRepository.save(address);
    return "redirect:/users";
}

修改后的Thymeleaf表单头部:

<form
    method="post"
    role="form"
    th:action="@{/address/save}"
    th:object="${addr}" <!-- 修改此处 -->
>

2. 清理冗余代码

  • 删除Address实体类中手动编写的所有getter/setter方法,保留@Getter@Setter注解即可。
  • 删除表单中所有手动添加的name属性,由th:field自动生成,避免重复定义。

3. 可选优化

将实体类中的add_id字段名改为id(符合JPA命名规范),同时调整对应的th:field="*{id}",进一步减少潜在的命名冲突风险。

内容的提问来源于stack exchange,提问作者sushil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 11:20:24