Thymeleaf form passing all but one inputs to java controller - java

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.

Related

How can I write only long number as String on form input?

I am making a registration in Spring Boot using Spring Data JPA. I'm facing a problem in placing long values in a form field.
Model class: I have used #NotBlank annotation before private long nsuID, but when the form redirects the nsuID field has filled up by 0.
#Entity
#Table(name="alumnies",uniqueConstraints = #UniqueConstraint(columnNames = "email") )
public class Alumni {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="NSU_ID")
#NotBlank
private long nsuID;
public long getNsuID() {
return nsuID;
}
public void setNsuID(long nsuID) {
this.nsuID = nsuID;
}
}
HTML file:
<div class="form-group"
th:classappend="${#fields.hasErrors('nsuID')}? 'has-error':''">
<label for="nsuID" class="control-label">NSU ID</label> <input
id="nsuID" class="form-control" th:field="*{nsuID}" />
<p class="error-message"
th:each="error : ${#fields.errors('nsuID')}"
th:text="${error}">Validation error</p>
</div>
Controller class:
#Controller
public class RegistraionController {
#Autowired
private UserService us;
#GetMapping("/")
public String showRegistration(Model model) {
Alumni alumni = new Alumni();
model.addAttribute("alumni", alumni);
return "registration";
}
#PostMapping("/registration")
public String saveAlumni(#ModelAttribute("alumni") #Validated Alumni alumni, BindingResult bindingResult) {
Alumni existing = us.findByEmail(alumni.getEmail());
if (existing != null) {
bindingResult.rejectValue("email", null, "There is already an account registered with that email");
}
if (bindingResult.hasErrors()) {
return "registration";
}
us.saveAlumni(alumni);
return "redirect:/";
}
}
As #Dip Halani said, long type variable can't hold null value since it's primitive type variable and long type variable's default value is 0.
And your #NotBlank constraint on nsuID is wrong because it checks that a character sequence's trimmed length is not empty it's usually used for validate given string value must be not null and the trimmed length must be greater than zero.
If you want to validate for non-null value then use #NotNull and replace long nsuID to Long nsuID.
See Difference Between #NotNull, #NotEmpty, and #NotBlank Constraints in Bean Validation
long can not hold null values so it might be converting to 0. try changing it to Long (wrapper class).
#NotBlank
private Long nsuID;

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

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;
}

Play 2.x: Binding model which contains a list to the template

I have two models User and Address.
public class User extends Model{
public String name;
public List<Address> addresses;
}
public class Address extends Model{
public String street;
public String house;
}
I have a template to add a user.
#(form: Form[User])
#helper.form(action=routes.User.add())
{
<label>Add User<label>
#text(form("name"), label="Name")
<input type="submit" class="btn btn-primary" value="Save" />
}
In my controller I have the add method.
public static Results add()
{
Form<User> filledForm = userForm.bindFromRequest();
User user = filledForm.get();
}
I have skipped unnecessary information like the error handling.
Now how can I bind the addresses? I want to be able to add multiple addresses in the form.
In Play 1.1 I have seen things like POJO collections but I am using 2.3.
As mentioned in comment probably JavaScript and AJAX would be most comfortable solution, you can i.e. create an User as unactive and activate him only when at least one address is added later.
Other solution can be using custom class with fields from both (User and Address) so after validating all required fields you can create an User and associated Address.
Sample
User model:
public class User extends Model {
public String firstName;
public String lastName;
#OneToMany(mappedBy = "user")
public List<Address> addresses; // This field doesn't keep relations it informs that relation is kept in `user` field of `Address` model
}
Address model:
public class Address extends Model {
#ManyToOne
public User user;
public String street;
public String house;
}
UserAdmin controller:
public static class UserWithAddress { //This is our class for validating data for user and his `primary` address
// User's fields
#Required
public String firstName;
#Required
public String lastName;
// Address' fields
#Required
public String street;
#Required
public String house;
}
public static Result addUserWithAddress() {
Form<UserWithAddress> userForm = Form.form(UserWithAddress.class);
return ok(userWithAddressView.render(userForm));
}
public static Result saveUserWithAddress() {
Form<UserWithAddress> userForm = Form.form(UserWithAddress.class).bindFromRequest();
if (userForm.hasErrors()) {
return badRequest(userWithAddressView.render(userForm));
}
// form is valid, let's extract data from form, create and save both objects
UserWithAddress data = userForm.get();
User user = new User();
user.firstName = data.firstName;
user.lastName = data.lastName;
user.save(); // Save new user before creating new Address, cause we need a new ID to associate...
Address address = new Address();
address.user = user;
address.street = data.street;
address.house = data.house;
address.save();
return ok("Hooray you're registered now! And we know where you live ;)");
}
userWithAddressView.scala.html view
#(userForm: Form[controllers.UsersAdmin.UserWithAddress])
#main(title = "Input your basic data and address data") {
#helper.form(action=routes.UsersAdmin.saveUserWithAddress()){
#helper.inputText(userForm("firstName"))
#helper.inputText(userForm("lastName"))
#helper.inputText(userForm("street"))
#helper.inputText(userForm("house"))
<input type="submit" value="Save"/>
}
}
So I built it using the #helper.repeat. The best example is in the samples provided by Play. It's called forms.
For a quick reference, the description can be found here - https://www.playframework.com/documentation/2.2.x/JavaFormHelpers
#helper.inputText(userForm("name"))
#helper.repeat(userForm("emails"), min = 1) { emailField =>
#helper.inputText(emailField)
}
For the address example:
#(form: Form[User])
#helper.form(action=routes.User.add())
{
<label>Add User<label>
#text(form("name"), label="Name")
#helper.repeat(form("addresses"), min= 1){ address =>
#helper.inputText(address("street"))
#helper.inputText(address("house"))
}
<input type="submit" class="btn btn-primary" value="Save" />
}
Validation works as normal if you add the required annotaion over street and house. The only thing you need to do is add the #Valid (javax.validation.Valid) annotation over the list of addresses.
#Valid
public List<Address> addresses;
For adding and removing addresses, the sample is the best to look through.

Getting a list of objects in model and implementing into ng-repeat

I have a webapp and two models which I would like to use. One model is Trainees and another one is TraineeStatus. I would like to get an array or a list of TraineeStatus inside Trainees and then pass it to AngularJS view an display it inside select method, what is the best approach to do it?
Trainees.java
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#ManyToOne
private TraineeStatus traineestatus;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
public Trainees(){
}
public Trainees(String name, String surname, String phoneDetails, String email, int id, int groupsID, TraineeStatus traineestatus, int customersID) {
super();
this.name = name;
this.surname = surname;
this.email = email;
this.phoneDetails = phoneDetails;
this.groupsID = groupsID;
this.traineestatus = traineestatus;
this.customersID = customersID;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Trainees){
Trainees contact = (Trainees) object;
return contact.traineesID == traineesID;
}
return false;
}
#Override
public int hashCode() {
return traineesID;
}
}
TraineeStatus.java
#Entity
#Table(name="traineeStatus")
public class TraineeStatus {
#Id
#GeneratedValue
private int traineeStatusId;
private String value;
public TraineeStatus(){
}
public TraineeStatus(String value, int id) {
super();
this.value = value;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof TraineeStatus){
TraineeStatus value = (TraineeStatus) object;
return value.traineeStatusId == traineeStatusId;
}
return false;
}
#Override
public int hashCode() {
return traineeStatusId;
}
}
My view:
<div class="input-append">
<select
required
ng-model="contact.traineestatus"
name="traineestatus.id"
placeholder="<spring:message code='sample.status'/> ">
<option ng-repeat="trainee in contact.traineestatus" value="{{trainee.id}}">{{trainee.value}}</option>
</select>
</div>
<div class="input-append">
<label>
<span class="alert alert-error"
ng-show="displayValidationError && updateContactForm.traineestatus.id.$error.required">
<spring:message code="required"/>
</span>
</label>
</div>
Currently with contact.traineestatus returns one object with id and value, but I would like to get an array.
I am trying to utilize the object, so I would have a select function, where it displays the value and would save the Id and save it in the Trainees table.
Is there a way to utilize this object and use it in my select? Thanks in regards!
This is a little difficult to explain, so I will try to keep it simple.
First of all you need to have in mind the the list of TraineeStatus is not going to be retrieved in the same request has the contact/trainees list, you will need a separate HTTP request to get that list.
The same way you have ContactRepository to get all the Trainees records, you will need a TraineeStatusRepository to get all the TraineeStatus records. Then, on one of your Services you would need a method to get the TraineeStatus list, like this:
private List<TraineeStatus> findAllTraineeStatus() {
return traineeStatusRepository.findAll();
}
After that you will need to define an HTTP API to access that list (i.e. GET http://example.com/traineeStatus). The idea is probably the same you have to retrieve the ContactList.
Then, on your AngularJS app, you would need to use a function to load the list of TraineeStatus. For example:
$scope.getTraineeStatus = function () {
// assuming the HTTP API is: GET http://example.com/traineeStatus
$http.get('http://example.com/traineeStatus', config)
.success(function (data) {
$scope.traineeStatus = data;
})
.error(function (err) {
// handle error
});
}
And then loop through the $scope.traineeStatus property on the ng-repeat:
<select
required
ng-model="contact.traineestatus"
name="traineestatus.id"
placeholder="<spring:message code='sample.status'/> ">
<option ng-repeat="status in traineeStatus" value="{{status.id}}">{{status.value}}</option>
</select>

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;
}

Categories

Resources