I have this dummy project Spring Boot with React, every time I send a POST request it does NOT hit the database and generates below error
Error:-
"JSON parse error: Cannot construct instance of `com.xx.xx.Category` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('4')"
I know it's the foreign key which seems to be the problem, I have been searching for a while threr are few similar threads but none of them are helping me.
POJO
@Data
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long prodId;
private String prodName;
private String prodDes;
private int prodQunt;
private Date createdDate = new Date();
public Product() {}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "catId")
private Category cat;
}
Controller
@RestController
@RequestMapping("/prods")
@CrossOrigin(origins = "http://localhost:3000")
public class ProductController {
@Autowired
ProductRepository pr;
@PostMapping("/add")
public Product addProd(@RequestBody Product prod) {
return pr.save(prod);
}
}
Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}
React
const ProdAdd = () => {
const [product, setProduct] = useState({ prodName: "", prodDes: "", prodQunt: "" });
const [catList, setCatList] = useState([]);
useEffect(() => {
fetch('http://localhost:8080/categories')
.then((res) => res.json())
.then(res => {
setCatList(res)
}).catch(err => console.log(err))
}, [])
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setProduct({ ...product, [name]: value });
}
const handleSubmit = async (e) => {
e.preventDefault();
console.log(product);
await fetch("http://localhost:8080/prods/add", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(product)
})
};
return (
<div>
<h1>Add New Product</h1>
<Form method='Post' onSubmit={handleSubmit}>
<Row>
<Col>
<Form.Control name='prodName' id='' onChange={handleChange} value={product.prodName} placeholder='Product Name' />
</Col>
<Col>
<Form.Control name='prodDes' id='' onChange={handleChange} value={product.prodDes} placeholder='Product Description' />
</Col>
<Col>
<Form.Control name='prodQunt' id='' onChange={handleChange} value={product.prodQunt} placeholder='Product Quntity' />
</Col>
<Col>
<Form.Select name='cat' id='' onChange={handleChange} value={product.cat}>
{catList.map((cat) => {
return (
<option key={cat.id} value={cat.id}>
{cat.catName}
</option>
);
})}
</Form.Select>
</Col>
</Row>
<p></p>
<Button variant="primary" type="submit">Submit</Button>
<p></p>
</Form>
</div>
);
};
export default ProdAdd;
From React, you are sending the id of the category but the REST endpoint is expecting a Category object. The Category constructor seems to exists but is not able to create the object with just the id as a String (what you are sending from React)
You have 2 options here:
In React, you have prodName, prodDes, prodQunt and cat as string. Create a Java object mapping those - let's say ProductDTO. Now, change the endpoint to
@Autowired
ProductRepository pr;
@Autowired
CategoryRepository cr;
@PostMapping("/add")
public Product addProd(@RequestBody ProductDTO prodDTO) {
long catId = Long.parseLong(prodDto.getCat()); // should be validated before
Category cat = cr.getOne(catId);
Product prod = new Product();
prod.setProdName(prodDTO.getProdName);
prod.setProdDes(prodDTO.getProdDes);
prod.setProdQunt(Integer.parseInt(prodDTO.getProdQunt)); // should be validated before
prod.setCat(cat); // <-- updated
return pr.save(prod);
}