Thymeleaf Spring JPA One to Many type mismatch - spring

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]]

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.

The error stacktrace shows that there is a type mismatch for the id field in Category entity :
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;
Thymeleaf might be sending this value as you have provided th:value="${cat.id}" as the value. But when spring tries to map the value to the Product entity it expects an Integer value I think from the stacktrace.

Related

When I put the list with the specific drugs on the controller I have problem but when I put the name of drugs in mySQL I don't have

2023-01-28 13:55:33.706 WARN 17396 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'patient' on field 'drugs': rejected value [panadol]; codes [typeMismatch.patient.drugs,typeMismatch.drugs,typeMismatch.java.util.Set,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [patient.drugs,drugs]; arguments []; default message [drugs]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Set' for property 'drugs'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'panadol'; nested exception is java.lang.NumberFormatException: For input string: "panadol"]]
package com.example.prescription.model;
import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
#Entity
#Table(name = "patients")
public class Patient implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "dob")
private String dateOfBirth;
#NotEmpty(message = "Phone number may not be empty")
#Size(min = 10, max = 10)
#Column(name = "phone")
private String phone;
#NotEmpty(message = "Email may not be empty")
#Size(min = 7, max = 50)
#Column(name = "email")
private String email;
#Column(name = "fathers_name")
private String fathersName;
#Column(name = "mothers_name")
private String mothersName;
#Column(name = "amka")
#Size(min = 11, max = 11)
#Pattern(regexp = "^[0-9]+$", message = "AMKA must contain only numbers")
private String amka;
#Column(name = "id_card")
#Pattern(regexp = "^[a-zA-Z0-9]+$", message = "ID must contain only letters and numbers")
private String idCard;
#Column(name = "city")
private String city;
#Column(name = "postal_code")
#Size(min = 5, max = 5)
#Pattern(regexp = "^[0-9]+$", message = "PC must contain only numbers")
private String postalCode;
#Column(name = "symptoms")
private String symptoms;
#Column(name = "pharmacy")
private String pharmacy;
#Column(name = "doctor_name")
private String doctorsName;
#Column(name = "message")
private String message;
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinTable(name = "patient_drug",joinColumns = #JoinColumn(name = "patient_id"),
inverseJoinColumns = #JoinColumn(name = "drug_id"))
private Set<Drug> drugs;
public Patient(Patient patient, Drug drug, Date date) {
}
public Patient() {
}
public void addDrug(Drug drug){
this.drugs.add(drug);
drug.getPatients().add(this);
}
public void removeDrug(Drug drug) {
this.drugs.remove(drug);
drug.getPatients().remove(this);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(String dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFathersName() {
return fathersName;
}
public void setFathersName(String fathersName) {
this.fathersName = fathersName;
}
public String getMothersName() {
return mothersName;
}
public void setMothersName(String mothersName) {
this.mothersName = mothersName;
}
public String getAmka() {
return amka;
}
public void setAmka(String amka) {
this.amka = amka;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getSymptoms() {
return symptoms;
}
public void setSymptoms(String symptoms) {
this.symptoms = symptoms;
}
public String getPharmacy() {
return pharmacy;
}
public void setPharmacy(String pharmacy) {
this.pharmacy = pharmacy;
}
public String getDoctorsName() {
return doctorsName;
}
public void setDoctorsName(String doctorsName) {
this.doctorsName = doctorsName;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Set<Drug> getDrugs() {
return drugs;
}
public void setDrugs(Set<Drug> drugs) {
this.drugs = drugs;
}
public void removeDrugs() {
Iterator<Drug> iterator = this.drugs.iterator();
while (iterator.hasNext()) {
Drug drug = iterator.next();
drug.getPatients().remove(this);
iterator.remove();
}
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Patient patient = (Patient) o;
return Objects.equals(id, patient.id) && Objects.equals(firstName, patient.firstName) && Objects.equals(lastName, patient.lastName) && Objects.equals(dateOfBirth, patient.dateOfBirth) && Objects.equals(phone, patient.phone) && Objects.equals(email, patient.email) && Objects.equals(fathersName, patient.fathersName) && Objects.equals(mothersName, patient.mothersName) && Objects.equals(amka, patient.amka) && Objects.equals(idCard, patient.idCard) && Objects.equals(city, patient.city) && Objects.equals(postalCode, patient.postalCode) && Objects.equals(symptoms, patient.symptoms) && Objects.equals(pharmacy, patient.pharmacy) && Objects.equals(doctorsName, patient.doctorsName) && Objects.equals(message, patient.message) && Objects.equals(drugs, patient.drugs);
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder("Patient{");
sb.append("id=").append(id);
sb.append(", firstName='").append(firstName).append('\'');
sb.append(", lastName='").append(lastName).append('\'');
sb.append(", dateOfBirth='").append(dateOfBirth).append('\'');
sb.append(", phone='").append(phone).append('\'');
sb.append(", email='").append(email).append('\'');
sb.append(", fathersName='").append(fathersName).append('\'');
sb.append(", mothersName='").append(mothersName).append('\'');
sb.append(", amka='").append(amka).append('\'');
sb.append(", idCard='").append(idCard).append('\'');
sb.append(", city='").append(city).append('\'');
sb.append(", postalCode='").append(postalCode).append('\'');
sb.append(", symptoms='").append(symptoms).append('\'');
sb.append(", pharmacy='").append(pharmacy).append('\'');
sb.append(", doctorsName='").append(doctorsName).append('\'');
sb.append(", message='").append(message).append('\'');
sb.append('}');
return sb.toString();
}
}
package com.example.prescription.controller;
import com.example.prescription.model.Drug;
import com.example.prescription.model.Patient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import com.example.prescription.service.DrugService;
import com.example.prescription.service.PatientService;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
#Controller
public class PatientController {
private final PatientService patientService;
private final DrugService drugService;
public PatientController(#Autowired PatientService patientService,
#Autowired DrugService drugService) {
this.patientService = patientService;
this.drugService = drugService;
}
//read
#GetMapping("/allPatients")
public String getAllPatients(Model model) {
List<Patient> patientList = patientService.getAllPatients();
model.addAttribute("patientList", patientList);
return "patients";
}
//edit
#GetMapping("/newPatient")
public ModelAndView register() {
ModelAndView mav = new ModelAndView("patientForm");
mav.addObject("patient", new Patient());
mav.addObject("drugs", drugService.getAllDrugs());
return mav;
}
//save
#PostMapping("/patient/save")
public String savePatient(Patient patient) {
patientService.savePatient(patient);
return "redirect:/allPatients";
}
//update
#GetMapping("/editPatient/{id}")
public ModelAndView editPatient(#PathVariable(value = "id") String id) {
ModelAndView mav = new ModelAndView("patientFormEditToUpdate");
Long pid = Long.parseLong(id);
Patient formPatient = patientService.findPatientById(pid);
mav.addObject("patient", formPatient);
return mav;
}
#PostMapping("/updatePatient/patient/{id}")
public String updatePatient(#PathVariable(value = "id") String id, Patient patient) {
Long pid = Long.parseLong(id);
Patient patient1 = patientService.findPatientById(pid);
patient1 = patient;
patientService.updatePatient(patient1);
return "redirect:/allPatients";
}
//delete
#GetMapping("/delete/{id}")
public String deleteById(#PathVariable(value = "id") String id) {
Long pid = Long.parseLong(id);
Patient deletedPatient = patientService.findPatientById(pid);
patientService.deletePatient(deletedPatient);
return "redirect:/allPatients";
}
#GetMapping("/prescribeDrugs/{id}")
public ModelAndView prescribeDrugs(#PathVariable("id") String id) {
Long pid = Long.parseLong(id);
ModelAndView mav = new ModelAndView("patientFormEdit");
Patient formPatient = patientService.findPatientById(pid);
mav.addObject("patient", formPatient);
mav.addObject("drugs", drugService.getAllDrugs());
mav.addObject("drugList",drugList);
return mav;
}
static List<String> drugList= null;
static{
drugList = new ArrayList<>();
drugList.add("depon");
drugList.add("aspirin");
drugList.add("panadol");
}
#PostMapping("/prescribeDrugs/Patient/{id}")
public String prescribePatientDrugs(#Valid Patient patient,String id, #ModelAttribute(value = "drugs")Long drugId ,BindingResult result)
{
if(result.hasErrors())
{
return "patients";
}
try {
Long pId = Long.parseLong(id);
Patient formPatient = patientService.findPatientById(pId);
Drug drug = drugService.findById(drugId);
formPatient.setCity(patient.getCity());
formPatient.setEmail(patient.getEmail());
formPatient.setPhone(patient.getPhone());
formPatient.setSymptoms(patient.getSymptoms());
formPatient.setPharmacy(patient.getPharmacy());
formPatient.setDoctorsName(patient.getDoctorsName());
formPatient.setMessage(patient.getMessage());
Drug patientDrug= new Drug(patient, drug, new Date());
drugService.save(drug);
formPatient.getDrugs().add(patientDrug);
patientService.updatePatient(formPatient);
}catch (NumberFormatException numberFormatException){
System.out.println("error");
}
return "redirect:/allPatients";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<link th:href="#{/css/webform.css}" href="/css/webform.css" rel="stylesheet" type="text/css"/>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>Update Patient</title>
</head>
<script type="text/javascript" th:src="#{/js/webform.js}"></script>
<form th:action="#{/prescribeDrugs/Patient/{id}(id = ${patient.id})}" method="post" th:object="${patient}">
<div class="container prescription-form">
<div class="row">
<div class="col-lg-12 col-12">
<form>
<h1>Electronic Prescription Form</h1>
<div class="row">
<div class="col-lg-6 col-12">
<label>
<span>Patient Name</span><input type="text" th:value="${patient.firstName}"
th:name="firstName" disabled/>
</label>
</div>
<div class="col-lg-6 col-12">
<label>
<span>Patient Email</span><input id="email" type="text" th:value="${patient.email}"
th:name="email"/>
</label>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-12">
<label>
<span>Patient Surname</span><input id="surname" type="text"
th:value="${patient.lastName}" th:name="surname"
disabled/>
</label>
</div>
<div class="col-lg-6 col-12">
<label>
<span>Patient Phone</span><input id="phone" type="text" th:value="${patient.phone}"
th:name="phone"/>
</label>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-12">
<label>
<span>Patient Symptoms</span><input id="symptoms" type="text" th:name="symptoms"
required/>
</label>
<span class="error_message">This field is required</span>
</div>
<span class="error_message">This field is required</span>
</div>
<div class="col-lg-6 col-12" >
<label>
<span>Drug*</span>
<select name="drugs">
<option th:each="drug : ${drugList}"
th:text="${drug}">
</select>
</label>
<span class="error_message">This field is required</span>
</div>
<div class="row">
<div class="col-lg-6 col-12">
<label>
<span>AMKA</span><input id="amka" type="text" th:value="${patient.amka}" th:name="amka"
disabled/>
</label>
</div>
<div class="col-lg-6 col-12">
<label>
<span>Patient ID</span><input id="patient_id" type="text" th:value="${patient.idCard}"
th:name="patient_id" disabled/>
</label>
<span class="error_message">This field is required</span>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-12">
<div class="message pharmacy">
<label class="miniTextfield">
<span>Pharmacy to deliver</span><textarea id="pharmacy" th:name="pharmacy" required></textarea>
</label>
<span class="error_message">This field is required</span>
</div>
</div>
<div class="col-lg-6 col-12">
<div class="message signature">
<label class="miniTextfield">
<span>Doctor Signature</span><textarea id="doctorSignature" th:name="doctorsName" required></textarea>
</label>
<span class="error_message">This field is required</span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-12">
<label>
<span>City</span><input id="city" type="text" th:value="${patient.city}" th:name="city"
/>
</label>
</div>
</div>
<div class="message">
<div class="col-lg-12 col-12">
<label class="message_btn_wrapper">
<span>Message</span><textarea id="feedback" th:name="message"></textarea>
<input type="submit" value="Submit Form"/>
<div class="requiredMessage">Fields with * are mandatory</div>
</label>
</div>
</div>
</form>
</div>
</div>
</div>
</form>
In your controller, you are adding a list of Drug objects of type String, and they are not of type Drug. Your Patient is expecting a Set of Drug objects.
You have several other issues in this code too, but create a new Set of Drug objects, either using a constructor directly or using a Builder pattern.
E.g.: like:
Set.of(new Drug("depon"), new Drug("aspirin"), new Drug("panadol"));
But adjusted for your Drug constructor.
Take a look at my public repo for some working code.
https://github.com/vphilipnyc/For_Vasileios_Maziotis

How to populate dropdown list from one entity class to another in spring-boot?

Customer.java file
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String relative;
private String address;
private Long aadhar;
private Long contact;
public Long getAadhar() {
return aadhar;
}
public void setAadhar(Long aadhar) {
this.aadhar = aadhar;
}
#ManyToOne
#JoinColumn(name="town_name",insertable = false,updatable = false)
private Town town;
private String town_name;
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 String getRelative() {
return relative;
}
public void setRelative(String relative) {
this.relative = relative;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Town getTown() {
return town;
}
public void setTown(Town town) {
this.town = town;
}
public String getTown_name() {
return town_name;
}
public void setTown_name(String town_name) {
this.town_name = town_name;
}
public Long getContact() {
return contact;
}
public void setContact(Long contact) {
this.contact = contact;
}
}
Town.Java
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Town {
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Id
private String townname;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTownname() {
return townname;
}
public void setTownname(String townname) {
this.townname = townname;
}
}
CustomerController.java file
#Controller
public class CutomerController {
#Autowired
private CutomerService customerService;
#GetMapping("/customer")
public String findAllCustomers(Model model) {
model.addAttribute("customers", customerService.findAllCustomers());
return "customer";
}
#PostMapping("/customer/addnew")
public String addNew(Customer customer) {
customerService.saveCustomer(customer);
return "redirect:/customer";
}
}
TownController.java file
#Controller
public class TownController {
#Autowired
private TownService townService;
#GetMapping("/town")
public String findAllTowns(Model model) {
model.addAttribute("towns", townService.findAllTown());
return "town";
}
}
My customer.html file
<!-- Multi Columns Form -->
<form class="row g-3" th:action="#{/customer/addnew}" method="post">
<div class="col-md-4">
<label for="aadhar" class="form-label">Aadhar No.</label>
<input type="number" min="0" max="999999999999" class="form-control" id="aadhar">
</div>
<div class="col-md-8">
<label for="customername" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="name"onKeyup="this.value = this.value.toUpperCase()" required>
</div>
<div class="col-md-6">
<label for="relative" class="form-label">S/O,D/O,C/O</label>
<input type="text" class="form-control" id="relative"onKeyup="this.value = this.value.toUpperCase()" required>
</div>
<div class="col-md-6">
<label for="contact" class="form-label">Contact No.</label>
<input type="number" max="9999999999" class="form-control" id="contact">
</div>
<div class="col-12">
<label for="inputAddress5" class="form-label">Address</label>
<input type="text" class="form-control" id="address"onKeyup="this.value = this.value.toUpperCase()" placeholder="1234 Main St" required>
</div>
<div class="col-md-4">
<label for="inputTown" class="form-label" id="selecttown">Town/Area</label>
<select class="form-control" id="selecttown" name="townname" th:field="*{townname}" required>
<option selected>Choose...</option>
<option th:each="town:${towns}" th:value="${town.towname}" th:text="${town.towname}"></option>
</select>
</div>
<div class="text-center" style="margin-bottom:10px">
<button type="submit" class="btn btn-primary mx-1 my-1">Submit</button>
i am expecting populate the names of towns in dropdown list of customer modal form. But i am getting this error
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Nov 30 12:18:23 IST 2022
There was an unexpected error (type=Internal Server Error, status=500).
An error happened during template parsing (template: "class path resource [templates/customer.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/customer.html]")
Caused by: org.attoparser.ParseException: Error during execution of processor 'org.thymeleaf.spring6.processor.SpringSelectFieldTagProcessor' (template: "customer" - line 473, col 70)
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring6.processor.SpringSelectFieldTagProcessor' (template: "customer" - line 473, col 70)
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'townname' available as request attribute
Your are referencing wrong variable.
Modify your CustomerController class like this:
#Autowired
private CutomerService customerService;
#Autowired
private TownService townService;
#GetMapping("/customer")
public String findAllCustomers(Model model) {
model.addAttribute("customers", customerService.findAllCustomers());
model.addAttribute("towns", townService.findAllTown());
return "customer";
}
#PostMapping("/customer/addnew")
public String addNew(Customer customer) {
customerService.saveCustomer(customer);
return "redirect:/customer";
}

Bidirectional OneToMany-ManyToOne Relationship referencing unsaved transient instance (Spring MVC - Thymeleaf)

new here. I'm new to Spring and Thymeleaf, I'm trying to learn by following a video and I don't know why I get the following exception (org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : org.launchcode.codingevents.models.Event.eventCategory -> org.launchcode.codingevents.models.EventCategory) when I try to creat an Event giving it an EventCategory in the Thymeleaf form. I tried cascading from one side, then from the other and then from both, but it didn't work.
I'll be immensely grateful with whoever helps me out.
Here's my code.
#MappedSuperclass
public abstract class AbstractEntity {
#Id
#GeneratedValue
private int id;
public int getId() {
return id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AbstractEntity entity = (AbstractEntity) obj;
return this.id == entity.id;
}
#Entity
public class Event extends AbstractEntity {
#NotBlank(message = "Name is required")
#Size(min = 3, max = 50, message = "Name must be between 3 and 50 characters")
private String name;
#Size(max = 500, message = "Description too long!")
private String description;
#NotBlank(message = "Email is required")
#Email(message = "Invalid email. Try again")
private String contactEmail;
#ManyToOne
#NotNull(message = "Category is required")
private EventCategory eventCategory;
public Event() {
}
public Event(String name, String description, String contactEmail, EventCategory eventCategory) {
this.name = name;
this.description = description;
this.contactEmail = contactEmail;
this.eventCategory = eventCategory;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getContactEmail() {
return contactEmail;
}
public void setContactEmail(String contactEmail) {
this.contactEmail = contactEmail;
}
public EventCategory getEventCategory() {
return eventCategory;
}
public void setEventCategory(EventCategory eventCategory) {
this.eventCategory = eventCategory;
}
#Override
public String toString() {
return name;
}
#Entity
public class EventCategory extends AbstractEntity implements Serializable {
#Size(min = 3, message = "Name must be at least 3 characters long")
private String name;
#OneToMany(mappedBy = "eventCategory")
private final List<Event> events = new ArrayList<>();
public EventCategory() {
}
public EventCategory(#Size(min = 3, message = "Name must be at least 3 characters long") String name) {
this.name = name;
}
public List<Event> getEvents() {
return events;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
#Controller
#RequestMapping("events")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private EventCategoryRepository eventCategoryRepository;
#GetMapping
public String displayAllEvents(#RequestParam(required = false) Integer categoryId, Model model) {
if (categoryId == null) {
model.addAttribute("title", "All Events");
model.addAttribute("events", eventRepository.findAll());
} else {
Optional<EventCategory> result = eventCategoryRepository.findById(categoryId);
if (!result.isPresent()) {
model.addAttribute("title", "Invalid Category Id: " + categoryId);
} else {
EventCategory category = result.get();
model.addAttribute("title", "Events in Category: " + category.getName());
model.addAttribute("events", category.getEvents());
}
}
return "events/index";
}
// Lives at /events/create
#GetMapping("create")
public String displayCreateEventForm(Model model) {
model.addAttribute("title", "Create Event");
model.addAttribute(new Event());
model.addAttribute("categories", eventCategoryRepository.findAll());
return "events/create";
}
// lives at /events/create
#PostMapping("create")
public String processCreateEventForm(#Valid #ModelAttribute("newEvent") Event newEvent, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute("title", "Create Event");
return "events/create";
}
model.addAttribute("events", eventRepository.findAll());
eventRepository.save(newEvent);
return "redirect:";
}
// lives at /events/delete
#GetMapping("delete")
public String displayDeleteEventForm(Model model) {
model.addAttribute("title", "Delete Events");
model.addAttribute("events", eventRepository.findAll());
return "events/delete";
}
// lives at /events/delete
#PostMapping("delete")
public String processDeleteEventForm(#RequestParam(required = false) int[] eventIds) {
if (eventIds != null) {
for (int id : eventIds) {
eventRepository.deleteById(id);
}
}
return "redirect:";
}
}
Create Event
<nav th:replace="fragments :: navigation"></nav>
<form method="post" th:action="#{/events/create}" th:object="${event}">
<div class="form-group">
<label>Name
<input class="form-control" th:field="${event.name}">
</label>
<p class="error" th:errors="${event.name}"></p>
</div>
<div class="form-group">
<label>Description
<input class="form-control" th:field="${event.description}">
</label>
<p class="error" th:errors="${event.description}"></p>
</div>
<div class="form-group">
<label>Contact Email
<input class="form-control" th:field="${event.contactEmail}">
</label>
<p class="error" th:errors="${event.contactEmail}"></p>
</div>
<div class="form-group">
<label>Category
<select th:field="${event.eventCategory}">
<option th:each="eventCategory : ${categories}" th:value="${eventCategory.id}"
th:text="${eventCategory.name}">
</option>
</select>
<p class="error" th:errors="${event.eventCategory}"></p>
</label>
</div>
<div th:replace="fragments :: create-button"></div>
</form>
As per your code you are only trying to save Event entity and ignoring EventCategory.
You need to set Event to EventCategory as well as EventCategory to Event and make the cascade save.
First add cascade property in Event entity as below.
#ManyToOne(cascade = CascadeType.ALL)
#NotNull(message = "Category is required")
private EventCategory eventCategory;
Then in the Controller make the following changes.
#PostMapping("create")
public String processCreateEventForm(#Valid #ModelAttribute("newEvent") Event newEvent, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute("title", "Create Event");
return "events/create";
}
model.addAttribute("events", eventRepository.findAll());
EventCategory eventCategory = newEvent.getEventCategory();
eventCategory.setEvent(newEvent);
eventRepository.save(newEvent);
return "redirect:";
}

Not mapping all fields from html to controller

I need to update my category object
my model:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
#Entity
public class Category {
#Id
#GeneratedValue
private int id;
#NotNull
private String name;
private String description;
#NotNull
private Long created;
private Long updated;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getCreated() {
return created;
}
public void setCreated(Long created) {
this.created = created;
}
public Long getUpdated() {
return updated;
}
public void setUpdated(Long updated) {
this.updated = updated;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
Here my controller:
#Controller
public class CategoryController {
private CategoryRepository categoryRepository;
private static Logger logger = LogManager.getLogger(CategoryController.class);
// If class has only one constructore then #Autowired wiil execute automatically
public CategoryController(CategoryRepository categoryRepository) {
this.categoryRepository = categoryRepository;
createStubCategoryList();
}
#PostMapping(value = "/category")
public String submitCategory(Category category, Model model) {
logger.info("updateCategory = " + category);
model.addAttribute("submitted", true);
model.addAttribute("category", category);
categoryRepository.save(category);
return "category";
}
#RequestMapping("category/edit/{id}")
public String editCategory(#PathVariable("id") int id, Model model) {
Optional<Category> category = categoryRepository.findById(id);
logger.info("find_category = " + category);
model.addAttribute("category", category);
return "category";
}
Here my template to edit category:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${appName}">Category template title</title>
<link th:href="#{/public/style.css}" rel="stylesheet"/>
<meta charset="UTF-8"/>
</head>
<body>
<div class="container">
<form method="post" action="#" th:object="${category}" th:action="#{/category}">
<h3>Category</h3>
<input type="text" placeholder="name" id="name" th:field="*{name}"/>
<textarea placeholder="Description of the category" rows="5" id="description"
th:field="*{description}"></textarea>
<input type="submit" value="Submit"/>
</form>
<div class="result_message" th:if="${submitted}">
<h3>Your category has been submitted.</h3>
<p>Find all categories here</p>
</div>
</div>
</body>
</html>
When call method in log has:
[INFO ] 2020-01-07 19:38:07.493 [http-nio-8090-exec-8] CategoryController - find_category = Optional[
Category{id=2, name='Electronics', created=1578418669105, updated=null, description='Electronics's description'}]
and here screen:
As you can see the field created=1578418669105
Nice.
Now I edit name "Electronics" to "Electronics2" and click submit.
As result call method: submitCategory in my controller. Nice.
Here result in log:
[INFO ] 2020-01-07 19:40:23.327 [http-nio-8090-exec-2] CategoryController - updateCategory =
Category{id=0, name='Electronics2', created=null, updated=null, description='Electronics's description'}
but as you can see the field created is null. Why?
I need to update only editable fields: name and description.
Another fields (like created, updated, id) must not change. This fields are not mapped.
How I can do this?
Because created, updated, id you need to pass as hidden. It is not available in html page.
Each column should be changed to updatable to false because by default is true.
#Column(name = "created", updatable = false)
private Long created;

Springs form null values

I have a small problem with my form in spring, it is a simple form with title, username and password, validation works as if I try to submit an empty form I get an error however if I input values in and submit the form, it is submitted but it displays that values inserted are "null".
Domain:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
#NotNull
#Size(min=2, max=30)
private String username;
#NotNull
#Size(min=6)
private String password;
public String getTitle() { return title; }
public String getUsername() { return username; }
public String getPassword() { return password; }
public Long getId() { return id; }
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) { this.password = password; }
public void setTitle(String title) { this.title = title; }
public List<Note> getNotes() {
return notes;
}
#OneToMany
private List<Note> notes = new ArrayList<Note>();
}
Form:
<form action="#" th:action="#{/create/user}" th:object="${user}" method="post">
<input type="hidden" th:field="*{id}" />
<p>Title: <input type="text" th:field="*{title}" /></p>
<p>Username: <input type="text" th:field="*{username}" /></p>
<p>Password: <input type="password" th:field="*{password}" /></p>
<p><input type="submit" value="Submit" /></p>
</form>
<div class="form-group">
<ol class="list-group">
<li class="list-group-item list-group-item-info" th:each="user : ${user}">
<span th:text=" ${user.title} + ${user.username} + ${user.password} "></span>
delete
</li>
</ol>
</div>
Controller:
#Controller
public class UserController {
#Autowired
protected UserService userService;
#RequestMapping(value = "/create/user", method = RequestMethod.POST)
public String createUser(Model model, #Valid #ModelAttribute("user") User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
model.addAttribute("user", user);
model.addAttribute("users", userService.findAll());
model.addAttribute("type", "danger");
model.addAttribute("message", "Please fill in all the fields" +
"Username needs to be at least 2 characters" +
"Passwords needs to contain at least 6 characters");
return "user";
}
userService.save(user);
model.addAttribute("user", new User());
model.addAttribute("cards", userService.findAll());
model.addAttribute("type", "success");
model.addAttribute("message", "A new user has been added");
return "user";
}
If any additional code in needed please let me know, I am still newbie in Spring
Found an error by reviewing the code:
In the form when outputting registered users it was:
<li class="list-group-item list-group-item-info" th:each="user : ${user}">
While it should be:
<li class="list-group-item list-group-item-info" th:each="user : ${users}">

Resources