In hibernate, how can JSP value match the variable in Entity class - java

I have a jsp page, some codes are below
<c:forEach var="tempCustomer" items="${customers}">
<c:url var="updateLink" value="/customer/showFormForUpdate">
<c:param name="customerId" value="${tempCustomer.id}"/>
</c:url>
<c:url var="deleteLink" value="/customer/delete">
<c:param name="customerId" value="${tempCustomer.id}"/>
</c:url>
<tr>
<!-- .firstName must match the variable in () customer setFirstName(String firstName) -->
<td> ${tempCustomer.firstName} </td>
<td> ${tempCustomer.lastName} </td>
<td> ${tempCustomer.email} </td>
<td>
Update
</td>
<td>
Delete
</td>
</tr>
</c:forEach>
And below is my entity class
package com.learning.spring.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="customer")
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int Id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="email")
private String email;
public Customer() {
}
public int getId() {
return Id;
}
public void setId(int 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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
As you can see,in JSP page, value="${tempCustomer.id}", I use id, which is lowercase i. However, in the entity class, I define it as below, which use Id, uppercase I.
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int Id;
So how can Hibernate map id to Id? And if I use Id instead in JSP page, the program throws Id not found exception.
In addition, I thought about case sensitive, but I found if I change ${tempCustomer.firstName} to ${tempCustomer.firstname}. it throws the firstname not found exception.
And help will be appreciated.

JSP EL uses java beans naming convention to convert names of properties to corresponding getters because it cannot access private fields directly. And according to Javabeans spec for both id and Id properties standard getter name is getId() that is why it works for both options. But for firstName getter is getFirstName() and for firstname getter is getFirstname(). Since you class has only getFirstName() that's why only firstName works in EL.
UPDATE
So I've checked the code. To get list of available properties that could be used in EL Java checks methods of the class. For getters like getSomeStuff() if removes first 3 letters (get) and then replaces first letter with lower case. So getSomeStuff() is converted to someStuff. And this is a name which could be used in JSP EL. Because of this validation id is a valid property and Id is not though proeprty Id is what actually exists in object. But JSP uses getters to get actual values. It doesn't check if actual properties exist and what are their names.
Pieces of code form java.beans.Introspector
if (argCount == 0) {
if (name.startsWith(GET_PREFIX)) {
// Simple getter
pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);
} else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
// Boolean getter
pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);
}
}
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
And code for java.beans.ProeprtyDescriptor
PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException {
if (bean == null) {
throw new IntrospectionException("Target Bean class is null");
}
setClass0(bean);
setName(Introspector.decapitalize(base));
setReadMethod(read);
setWriteMethod(write);
this.baseName = base;
}

Related

Cannot open JSP form page by a GET method whose model attribute name is the same as the form's one

When I open localhost:8080/customers/searchCustomer, it raises the exception:
org.springframework.beans.NotReadablePropertyException: Invalid property 'ssn' of bean class [java.lang.String]: Bean property 'ssn' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
My getter and setter are ok.
My entity:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
private String surname;
private String ssn;
private int age;
private String emailAddress;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}
in my controller:
#Controller
#RequestMapping("/customers")
public class CustomerController {
#Autowired
private CustomerService service;
// .....
#GetMapping("/searchCustomer")
public String searchCustomer(Model model) {
model.addAttribute("customerBySsnForm", new String());
return "searchCustomer";
}
#PostMapping("/showCustomer")
public String showCustomer(#ModelAttribute("customerBySsnForm") String ssn, Model model) {
Customer customer = service.getCustomerBySsn(ssn).get();
model.addAttribute("customerInShowCustomer", customer);
return "showCustomer";
}
}
in my service:
public Optional<Customer> getCustomerBySsn(String ssn){
return repository.findBySsn(ssn);
}
my repository:
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
public Optional<Customer> findBySsn(String ssn);
}
my searchCustomer.jsp:
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<body>
<h3>Search for customer</h3>
<form:form method="POST" action="showCustomer" modelAttribute="customerBySsnForm">
<table>
<tr>
<td><form:label path="ssn">Customer SSN</form:label></td>
<td><form:input path="ssn"/></td>
</tr>
<tr>
<td><input type="submit" value="Submit"/></td>
</tr>
</table>
</form:form>
</body>
</html>
EDIT 1:
my main methods (for customer insertion) in the controller are:
#GetMapping("/addCustomer")
public String addCustomer(Model model) {
Customer customerToAdd = new Customer();
model.addAttribute("customerForm", customerToAdd);
return "addCustomer";
}
#PostMapping("/addCustomerResult")
public String addCustomerResult(#ModelAttribute("customerForm") Customer customer) {
service.addCustomer(customer);
return "addCustomerResult";
}
When I enter the customer insertion form, everything is fine.
About the code, I understood that the Customer object created in the get method, added as attribute to Model, is going to be "populated" with its fields via the data I pass in the form. So I need an "entire" Customer object.
Indeed, for the case in which I get that error, trying to use the same modality of "transporting" objects via Model, I guess I need to add an attribute to Model with just a String object that would be set after I insert a string into the searchCustomer ssn text area.
I see this got me to error.
change model.addAttribute("customerBySsnForm", new String()); to model.addAttribute("customerBySsnForm", new Customer());
change #ModelAttribute("customerBySsnForm") String ssn to #ModelAttribute("customerBySsnForm") Customer customer
use customer.getSsn() to get value
When I posted my issue, I was unaware that Model class stores only objects designed to render the view. I finally solved my problem.
#GetMapping("/searchCustomer")
public String searchCustomer(Model model) {
model.addAttribute("customerBySsnForm", new Customer());
return "searchCustomer";
}
#PostMapping("/showCustomer")
public String showCustomer(#ModelAttribute("customerBySsnForm") Customer customer, Model model) {
String ssn = customer.getSsn();
customer = service.getCustomerBySsn(ssn).get();
model.addAttribute("customerInShowCustomer", customer);
return "showCustomer";
}

Thymeleaf form passing all but one inputs to java controller

My thymeleaf form has three inputs, when I submit it my controller is receiving the second and the third, but not the first one (first input is null)
I have similar forms and method to add records to a MySQL database which work with no problems.
I have diplayed the two first inputs in my controller, first (Id of the entity) is null, second is well informed
Here is my form:
addDepartment.html
<br/>
<form th:action="#{/university/addDepartment}"
th:object="${department}" method="POST">
Department Name
<input type="text" th:field="*{deptName}" />
<br/>
Department building
<input type="text" th:field="*{building}" />
<br/>
Budget
<input type="number" th:field="*{budget}" th:value=0/>
<br/>
<input type="submit" value="Add" />
</form>
<!-- Check if errorMessage is not null and not empty -->
<div th:if="${errorMessage}" th:utext="${errorMessage}"
style="color:red;font-style:italic;">
...
</div>
And my controller
// ADD a department to the database
// Show the addDepartment page
#RequestMapping(value= { "university/addDepartment" }, method = RequestMethod.GET)
public String showAddDepartment(Model model) {
Department department = new Department();
model.addAttribute("department", department);
return "addDepartment";
}
// Add the department to the database
#RequestMapping(value = { "university/addDepartment" }, method = RequestMethod.POST)
public String saveDepartment(Model model, //
#ModelAttribute("department")
#Validated Department department) {
System.out.println("DEPARTMENT: " + department.toString());
String deptName = department.getDeptName();
System.out.println("DEPARTMENT NAME: " + deptName);
String building = department.getBuilding();
System.out.println("BUILDING: " + building);
int budget = department.getBudget();
if (deptName != null && deptName.length() > 0 //
&& building != null && building.length() > 0 //
&& budget >= 0) {
Department newDepartment = new Department(deptName, building, budget);
departmentrepo.save(newDepartment);
return "redirect:/university/departmentList";
}
Object errorMessage = "All fields are mantatory";
model.addAttribute("errorMessage", errorMessage );
return "addDepartment";
}
Here is the Department class:
#Entity
#Table(name="department") // This annotation is needed only if the
table has a different name to this class
#EntityListeners(AuditingEntityListener.class)
public class Department {
#Id
private String deptName;
private String building;
private int budget;
//Constructors
public Department() {
}
public Department(String deptName, String building, int budget) {
this.deptName = deptName;
this.building = building;
this.budget = budget;
}
...GETTERS and SETTERS
The deptName is always null, but the other two inputs are ok
DEPARTMENT NAME: null
BUILDING: Main
UPDATE: I think I found the solution, but if someone can explain the reason...
I just passed the deptName in a #RequestParam annotation.
#RequestMapping(value = { "university/addDepartment" }, method =
RequestMethod.POST)
public String saveDepartment(Model model, //
#ModelAttribute("department") Department department,
#RequestParam("deptName") String deptName) {
//String deptName = department.getDeptName();
System.out.println("DEPARTMENT NAME: " + deptName);
String building = department.getBuilding();
System.out.println("BUILDING: " + building);
int budget = department.getBudget();
if (deptName != null && deptName.length() > 0 //
&& building != null && building.length() > 0 //
&& budget >= 0) {
Department newDepartment = new Department(deptName, building, budget);
departmentrepo.save(newDepartment);
return "redirect:/university/departmentList";
}
Object errorMessage = "All fields are mantatory";
model.addAttribute("errorMessage", errorMessage );
return "addDepartment";
}x`
There are a few issues with this code, but the first thing to try is removing the #Id annotation from your deptName property. Create a Long (or Integer) id property and annotate this instead. You can more simply call the department name property name too.
You will also want to make sure that you have the getters and setters done correctly, or more advisable - just use Project Lombok to get rid of extra text and potential for mistakes.
#Entity
#Table
#Data //see Project Lombok
public class Department {
#Id
private Long id;
private String name;
private String building;
private int budget; //I'd consider making this more precise by using BigDecimal
}
For your controller, let's more simply try this to see whether it is still breaking.
#GetMapping("/university/addDepartment")
public String showAddDepartment(Model model) {
model.addAttribute("department", new Department());
return "addDepartment";
}
#PostMapping("/university/addDepartment")
public String saveDepartment(#ModelAttribute("department") Department department) {
System.out.println("name="+department.getName()); //use a logger if you have one available
return "addDepartment";
}
In your form, you only need the th: notation if you want Thymeleaf to evaluate the logic. So you can change th:value=0 to value=0 if you really intend this.

How to map Form Parameter with Hibernate Entity Class in Spring-MVC Controller [duplicate]

This question already has an answer here:
Binding child object on submit spring mvc
(1 answer)
Closed 5 years ago.
I am new at Hibernate/JPA and I am trying to get form parameter with hibernate entity class. There was no problem with it until when I tried to get parameter with Entity class that has relationship with other class. For example;
Controller:
#RequestMapping(value = "/addProduct", method = RequestMethod.POST)
public String addProduct(Model model, Product product) {
databaseService.insert(product);
return "redirect:/products";
}
Entity class:
#Entity
#Table(name = "products")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private String id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private Category category;
#Column(name = "name")
private String name;
#Column(name = "price")
private String price;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
Category class :
#Entity
#Table(name = "categories")
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The program cannot set 'category'. Because category is not type like int, string.. I am realize the problem. But I cannot find solution to mapping parameters with Entity class. Is there any way to solve this. Or should I use #RequestParam to get parameters one-by-one instead of mapping parameters with entity class.
UPDATE
I just change category to category.id in my .jsp page and it solved my problem.
old code
<form>
...
<select class="form-control" name="category">
<c:if test="${not empty categoryList}">
<c:forEach var="item" items="${categoryList}">
<option value="${item.getId()}">${item.getName()}</option>
</c:forEach>
</c:if>
</select>
</form>
new code
<form>
...
<select class="form-control" name="category.id">
<c:if test="${not empty categoryList}">
<c:forEach var="item" items="${categoryList}">
<option value="${item.getId()}">${item.getName()}</option>
</c:forEach>
</c:if>
</select>
</form>
Please show us your form mapping,
Till then can could try with, change path in <form:select>/<form:input> tag to category.id and category.name
have a look at my another answer
I will suggest don't expose your Entity in the View, try to get form data in DTO, then convert to entity..
One way to do that is by creating a custom Spring Converter. So lets say you will be passing your entity's Id as a path variable, and your converter implementation would get that product object for you.
In your Controller you will need to do the following:
#RequestMapping(value = "/addProduct/{id}", method = RequestMethod.POST)
public String addProduct(Model model, #PathVariable("id") Product product) {
databaseService.insert(product);
return "redirect:/products";
}
Your Converter would look something like this:
import org.springframework.core.convert.converter.Converter;
public class StringToProductConverter implements Converter<String, Product> {
...
#Override
public Product convert(String id) {
Product product = databaseService.getProduct(id);
...
return product;
}
And don't forget to register your Converter either programmatically or by XML depending on your Spring version you're working on.

Getting java.lang.NumberFormatException when displaying data in JSP page

I am using Hibernate and Spring MVC. I am trying to fetch information from two tables and display it in a jsp page . Here are my tables:
Table Name : student
student_id studentName
1. Jason Stathum
Table Name : studentdetails
studentDetailsid FatherName MotherName student_id
1 Mr.X Mrs. Y 1
In my JSP page I am trying to display data like this:
Sl# Student Father Mother
1 Jason Stathum Mr.X Mrs.Y
When I run my application to see the data I get the following error message:
HTTP Status 500 - An exception occurred processing JSP page /WEB-INF/views/studentlist.jsp at line 29
root cause
java.lang.NumberFormatException: For input string: "FatherName"
java.lang.NumberFormatException.forInputString(Unknown Source)
java.lang.Integer.parseInt(Unknown Source)
java.lang.Integer.parseInt(Unknown Source).... and many other lines...
I have included my codes below, Could you please tell me what I am doing wrong?
Thank you very much
Entity: Student
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="student_id", nullable= false)
private Integer studentId;
private String studentName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="student_id", referencedColumnName="student_id")
private List<StudentDetails> studentDetails = new ArrayList<StudentDetails>();
// Getters and Setters
public Integer getStudentId() {
return studentId;
}
public void setStudentId(Integer studentId) {
this.studentId = studentId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public List<StudentDetails> getStudentDetails() {
return studentDetails;
}
public void setStudentDetails(List<StudentDetails> studentDetails) {
this.studentDetails = studentDetails;
}
Entity : StudentDetails
#Entity
#Table(name = "studentDetails")
public class StudentDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer studentDetailsId;
private String FatherName;
private String MotherName;
// Getters and Setters
public Integer getStudentDetailsId() {
return studentDetailsId;
}
public void setStudentDetailsId(Integer studentDetailsId) {
this.studentDetailsId = studentDetailsId;
}
public String getFatherName() {
return FatherName;
}
public void setFatherName(String fatherName) {
FatherName = fatherName;
}
public String getMotherName() {
return MotherName;
}
public void setMotherName(String motherName) {
MotherName = motherName;
}
Controller
#RequestMapping(value="studentlist", method = RequestMethod.GET)
public String getRecords(Model map)
{
List<Student> student = studentService.getAll();
map.addAttribute("student", student);
return "studentlist";
}
StudentDaoImpl
#Override
#SuppressWarnings("unchecked")
public List<Student> getAll() {
return getSessionFactory().openSession().createQuery("FROM Student").list();
}
JSP Page: studentlist
<c:if test="${!empty student}">
<table class="studentTable">
<tr>
<th>Sl#</th>
<th>Name</th>
<th>Father</th>
<th>Mother</th>
</tr>
<c:set var="count" value="0" scope="page" />
<c:forEach items="${student}" var="row">
<c:set var="count" value="${count + 1}" scope="page"/>
<tr>
<td>${count}</td>
<td>${row.studentName}</td>
<td>${row.studentDetails.FatherName}</td> <--- this is line 29
<td>${row.studentDetails.MotherName}</td>
</tr>
</c:forEach>
</table>
</c:if>
The problem is your trying to reference a property of an List object.
${row.studentDetails} = List<StudentDetails>
JSTL is trying to convert FatherName to a number so it can get that index in List<StudentDetails>.
To fix this you could do ${row.studentDetails[0].fatherName} or you could put a method in Student called something like getDefaultStudentData() that would return the first or "preffered" student details object then reference it like ${row.defaultStudentData.fatherName}. Or of course you could change your output to deal with multiple student details per Student ☺
In your JSP your variable row represents the Student object. So when you say row.studentDetails you are getting java.util.List now when you try to access anything on this list, then JSP assumes you are trying to fetch an element of the list using the provided index.
So it is like list.indexNumber, so JSP assumes FatherName and MotherName as number and tries to convert them to integer and gets the exception.
To fix this you have to iterate through the list:
<c:forEach items="${srow.studentDetails}" var="details">
${details.FatherName}
${details.MotherName}
</c:forEach>
Update:
As per naming convention you have to access the properties using details.fatherName and details.motherName
The standard way of declaring Java beans is to name the properties as fatherName instead of FatherName
private String fatherName;
private String motherName;
public String getFatherName() {
return fatherName;
}
public void setFatherName(String fatherName) {
this.fatherName = fatherName;
}
public String getMotherName() {
return motherName;
}
public void setMotherName(String motherName) {
this.motherName = motherName;
}

Obtaining ENUM description in Java based on value

I'm new to ENUMs so I want to make sure I'm doing all of this right before I perform a little surgery on my models. My ENUM has a shorter string that's stored in the database known as the name, and a description which is what the user sees. My objective is to get the description for a content object to show on the page.
Of course
<td valign='top'><strong>Paperless:</strong> ${content.getPaperless()}</td>
won't do it because that'll only show EDELIVERY_REQUIRED. How do I need to adjust this to make it function properly?
My edit page works great:
<td valign='top'>
<strong>Go Paperless Messaging</strong><br/>
<form:select path="paperless">
<form:options items="${paperlessEnumValues}" itemValue="name" itemLabel="description"/>
</form:select>
</td>
My enum:
public enum Paperless {
NONE(null, ""),
EDELIVERY_RECOMMENDED("EDELIVERY_RECOMMENDED", "Recommend eDelivery"),
EDELIVERY_REQUIRED("EDELIVERY_REQUIRED", "Require eDelivery"),
EDELIVERY_REQUIRED_JUSTIFICATION("EDELIVERY_REQUIRED_JUSTIFICATION", "Require eDelivery w/out justification");
private String name;
private String description;
Paperless(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return this.name;
}
public String getDescription() {
return this.description;
}
}
My Model
public class Content implements Serializable {
...
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 255)
#Column(name = "paperless")
private String paperless;
...
public String getPaperless() {
return paperless;
}
public void setPaperless(String paperless) {
this.paperless = paperless;
}
My content service
private List<Content> findContentEntities(boolean all, int maxResults, int firstResult) {
try {
CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
cq.select(cq.from(Content.class));
Query q = em.createQuery(cq);
if (!all) {
q.setMaxResults(maxResults);
q.setFirstResult(firstResult);
}
return q.getResultList();
} finally {
em.close();
}
}
Map private Paperless paperless in your entity, rather than a String. JPA supports enum mapping
Use ${content.paperless.description}

Categories

Resources