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"; }
问题原因
- 模型属性名与实体字段名冲突:Controller中
@ModelAttribute("address")的名称与Address实体类中的address字段名完全一致,导致Spring MVC参数绑定逻辑混乱。 - 表单提交时,名为
address的请求参数(对应实体的地址字符串值)被Spring错误识别为用于初始化Address类型模型对象的参数,进而尝试将字符串转换为Address实例,触发类型转换异常。 - 混乱的绑定逻辑同时导致地址字符串(如'madhapur')被错误绑定到
add_id(Long类型)字段,引发第二次类型转换错误。 - 额外问题:实体类已使用
@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
相关产品推荐
相关产品推荐

