Issue
I have a Spring Jpa project with a One to Many relationship. The error when submitting form is:
Field error in object 'product' on field 'category': rejected value [2]; codes [typeMismatch.product.category,typeMismatch.category,typeMismatch.com.example.demo.category.Category,typeMismatch];
Here the reject value [2] is the category_id. Why is thymeleaf sending id in the form. I also tried changing th:value=${cat}
Product
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(length = 128, nullable = false, unique = true)
private String name;
private float price;
@ManyToOne
@JoinColumn(name = "category_id")
private Category category;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
}
Category Class
@Entity
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(length = 45, nullable = false, unique = true)
private String name;
@OneToMany(mappedBy="category")
private Set<Product> products;
public Category() {
}
public Category(Integer id) {
this.id = id;
}
public Category(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Product> getProducts() {
return products;
}
public void setProducts(Set<Product> products) {
this.products = products;
}
}
Product Controller
@Controller
public class ProductController {
@Autowired
private ProductRepository productRepo;
@Autowired
private CategoryRepository categoryRepo;
@GetMapping("/products/new")
public String showNewProductForm(Model model) {
List<Category> listCategories = categoryRepo.findAll();
model.addAttribute("product", new Product());
model.addAttribute("listCategories", listCategories);
return "product_form";
}
@PostMapping("/products/save")
public String saveProduct(Product product) {
productRepo.save(product);
return "redirect:/";
}
}
Product Form Page
<body>
<div class="container text-center">
<div><h1>Create New Product</h1></div>
<form th:action="@{/products/save}" th:object="${product}" method="post" style="max-width: 600px; margin:0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-form-label col-sm-4">Product Name: </label>
<div class="col-sm-8">
<input type="text" th:field="*{name}" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-sm-4">Product Price: </label>
<div class="col-sm-8">
<input type="number" step="0.1" th:field="*{price}" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-sm-4">Category: </label>
<div class="col-sm-8">
<select th:field="*{category}" class="form-control" required>
<th:block th:each="cat: ${listCategories}">
<option th:text="${cat.name}" th:value="${cat.id}" />
**//I also tried changing to th:value="${cat}" but still get the same error //**
</th:block>
</select>
</div>
</div>
<div class="text-center p-3">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</form>
</div>
</body>
The error
WARN 8636 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'product' on field 'category': rejected value [2]; codes [typeMismatch.product.category,typeMismatch.category,typeMismatch.com.example.demo.category.Category,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [product.category,category]; arguments []; default message [category]];
default message [Failed to convert property value of type 'java.lang.String' to required type 'com.example.demo.category.Category' for property 'category';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@javax.persistence.ManyToOne @javax.persistence.JoinColumn com.example.demo.category.Category] for value '2';
nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Provided id of the wrong type for class com.example.demo.category.Category. Expected: class java.lang.Integer, got class java.lang.Long;
nested exception is java.lang.IllegalArgumentException: Provided id of the wrong type for class com.example.demo.category.Category. Expected: class java.lang.Integer, got class java.lang.Long]]
Solution
I found where I made my mistake. The issue was in my Category Repository class. Category class has an ID of Integer. However in the Category Repository, I defined the ID type as Long.
The mistake code
public interface CategoryRepository extends JpaRepository<Category, Long> {
}
The correct code
public interface CategoryRepository extends JpaRepository<Category, Integer> {
}
Just one thing is that the error stracktrace is a bit counter intuitive. Please give any suggestions as to how one could have traced back the error code to the repository class without going line by line of all codes.
Answered By - Pasang