Thymeleaf Spring Validation doesn't display - spring-boot

I am trying display error message on HTML which i got from Controller, but i dont see the message displaying.
I printed the ${#fields.hasErrors('name')} and i see it give me false
I debugged my code, and I found there is an error
com.gurnah.controller.ProductController : product size must be between 2 and 150
#RequestMapping(path = "/product")
public class ProductController {
private static final Logger logger = LoggerFactory.getLogger(ProductController.class);
#Autowired
private ProductRepo productRepo;
.....
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(#ModelAttribute #Valid Product product, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute("product", new Product());
// logger.info("Error Message " + bindingResult.getGlobalErrorCount());
logger.info("Error Message " + errors.getFieldErrorCount());
List<ObjectError> err = errors.getAllErrors();
for (ObjectError e : err) {
logger.info(e.getObjectName() + e.getDefaultMessage());
}
// logger.info("Error Message " + bindingResult.getErrorCount());
return "/prod/create";
} else {
productRepo.save(product);
return "redirect:/product/list";
}
}
My Pojo
#Entity
public class Product {
#Id
#Column(name = "ProdID", updatable = false, nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_generator")
#SequenceGenerator(name="product_generator", sequenceName = "zseq_product", allocationSize=1)
private int ProdID;
#NotNull(message = "Field Product cannot be empty")
// #NotEmpty(message = "Field Product cannot be empty")
#Size(min = 2, max = 150)
private String name;
private String prodDesc;
My HTML
<form th:object="${product}" th:action="#{/product/save}" method="post">
.....
<div class="form-group"
th:classappend="${#fields.hasErrors('name')}? 'has-error'">
<label th:for="name" class="col-form-label">Product Name:</label>
<input type="text" class="form-control" th:field="*{name}" />
<span th:if="${#fields.hasErrors('name')}"
th:errors="*{name}"
th:class="help-block">Title Errors</span>
</div>

Related

Thymeleaf form returns null values

when I submit the form, the pageBook.id and loggedUser.id values become null (in GetMapping method they have values). Any ideas why?
This is my form:
<div sec:authorize="isAuthenticated()">
<form th:action="#{/} + ${pageBook.id}" th:object="${transaction}" method="post">
<input type="hidden" th:field="${transaction.bookTransaction}" th:value="${pageBook.id}">
<input type="hidden" th:field="${transaction.userTransaction}" th:value="${loggedUser.id}">
<input type="submit" value="Submit!" />
</form>
</div>
My Entity:
#Entity
#Table(name = "transactions")
public class Transaction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="book_id", nullable=false)
private Book bookTransaction;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="user_id", nullable=false)
private User userTransaction;
... consturctors getters and setters
and my controller (pageBook and loggedUser aren't null):
#GetMapping("/{id}")
public String showBookPage(#AuthenticationPrincipal UserDetails userDetails, #PathVariable("id") Long id, Model model, RedirectAttributes redirectAttributes){
try {
Book pageBook = bookService.getBookById(id);
User loggedUser = (User) userService.loadUserByUsername(userDetails.getUsername());
model.addAttribute("transaction", new Transaction());
model.addAttribute("pageBook", pageBook);
model.addAttribute("loggedUser", loggedUser);
} catch (BookNoFoundException e) {
redirectAttributes.addFlashAttribute("message", e);
}
return "users/users_book_page";
}
#PostMapping("/{id}")
public String newTransaction(#PathVariable("id") Long id, #ModelAttribute("transaction") Transaction transaction){
log.info(transaction.getUserTransaction());
log.info(transaction.getBookTransaction());
transactionService.newTransaction(transaction);
return "redirect:/" + id;
}

Spring Controller throws NullPointerException when Pass Integer Data With Ajax/Post

I'm trying to pass set of data in html form;
<form class="form-inline" id="createjobform" method="POST" th:action="#{/createJob}">
...
<div class="form-group">
<input type="text" class="form-control" id="nopth"
placeholder="Number Of People To Hire" name="nopth" />
</div>
<div class="form-group">
<input type="text" readonly="readonly" class="form-control"
id="listid" placeholder="ID" name="listid"
title="ID of the list which associated"
th:value="${findOneList.id}"/>
</div>
listid in here, coming from another table (manytoone-onetomany) and I want to add new record with this listid. When I do it on phpmyadmin, it's working. But I want to do it with ajax post request or not without ajax, no matter actually. I tried both ways but it shows same error.
Here is my controller;
#RequestMapping(value = "/createJob", method = RequestMethod.POST)
public #ResponseBody void createJob(#RequestBody Jobs jobs,
#RequestParam(value = "title", required = false) String title,
#RequestParam(value = "description", required = false) String description,
#RequestParam(value = "nopth", required = false) Integer nopth,
#RequestParam(value = "lastDate", required = false) Date lastDate,
#RequestParam(value = "listid", required = false) Long listid,
HttpServletResponse hsr) throws IOException {
// if I do String nopth above and then
//jobs.setNopth(Integer.valueOf(nopth));
// error is NumberFormatException (cannot cast string to int)
jobs.setLastDate(lastDate);
jobs.setTitle(title);
jobs.setDescription(description);
jobs.setNopth(nopth);
Lists listttt = listsService.findOne(listid);
jobs.setLists(listttt);
jobsService.save(jobs);
mavHomepage.addObject("findOneList", listsService.findOne(jobs.getId()));
mavHomepage.addObject("mod", "VIEW_ONELIST");
hsr.sendRedirect("/oneList?id=" + listid);
}
so error is;
error: "Internal Server Error"
exception: "java.lang.NullPointerException"
message: "No message available"
path: "/createJob"
status: 500
at line jobs.setNopth(nopth);
also error is;
error: "Internal Server Error"
exception: "org.springframework.dao.InvalidDataAccessApiUsageException"
message: "The given id must not be null!; nested exception is
java.lang.IllegalArgumentException: The given id must not be null!"
path: "/createJob"
status: 500
at line Lists listttt = listsService.findOne(listid);
This is not with ajax/post. When I do;
public #ResponseBody void createJob(#RequestBody Jobs jobs,
#RequestParam(value="listid, required="false") listid,
HttpServletResponse hsr){
Lists listttt = listsService.findOne(listid);
jobs.setLists(listttt);
...
}
and ajax;
var formData = {
title : $("#title").val(),
description : $("#description").val(),
nopth : $("#nopth").val(),
lastDate : $("#lastDate").val(),
listid : $("#listid").val(),
}
...
$.ajax({
type : "POST",
contentType : "application/json",
url : "/createJob",
data : JSON.stringify(formData),
....
same error (The given id must not be null!)
SO HOW MUST I PASS DATA FROM FORM WHICH IS INTEGER/LONG VALUES ? WITH AJAX WOULD BE BETTER. AND THAT listid is FOREIGN KEY.
Model Classes;
#Entity(name = "jobs")
public class Jobs {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "Title")
private String title;
#Column(name = "Description")
private String description;
#Column(name = "Number_Of_People_To_Hire")
private Integer nopth;
#Column(name = "Last_Application_Date")
private Date lastDate;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="listid")
private Lists lists;
...
#Entity(name = "lists")
public class Lists implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#Column(name = "List_Name")
public String listname;
#OneToMany(mappedBy = "lists", cascade = CascadeType.ALL)
private List<Jobs> jobs;
I've tried to change type of nopth to String, Integer etc. and input type="text or number" no changes.
#RequestBody This annotation indicates a method parameter should be bound to the body of the web request
So you have to fix your controller like this:
#RequestMapping(value = "/createJob", method = RequestMethod.POST)
#ResponseBody
public void createJob(
#RequestBody Jobs jobs,
#RequestParam("listid") Long listid
HttpServletResponse hsr) throws IOException {
Lists listttt = listsService.findOne(listid);
jobs.setLists(listttt);
jobsService.save(jobs);
...
}
Your jquery request:
var formData = {
title : $("#title").val(),
description : $("#description").val(),
nopth : $("#nopth").val(),
lastDate : $("#lastDate").val()
}
var listid : $("#listid").val(),
var settings = {
"url": "http://localhost:8080/createJob?listid="+listid,
"method": "POST",
"data": JSON.stringify(formData)
"headers": {
"Content-Type": "application/json"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});

When update Employee table then why new row inserted in Address table. I want simple update Address table also

Note: When update Employee why new row inserted in Address table. I want to update Employee and want to update Address table also rather than new row inserted.
After execute following:
session=sessionFactory.getCurrentSession();
session.update(employee);
Result:
Hibernate: insert into Address (address, employee_empId, mobile, pincode) values (?, ?, ?, ?)
Hibernate: update Employee set addId=?, empName=?, empProfile=? where empId=?
#Entity
#Table(name = "Address")
public class Address {
#Id
#Column(name = "addId", unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
int addId;
#Column(name = "pincode", unique = false, nullable = false, length = 100)
int pincode;
#Column(name = "address", unique = false, nullable = false, length = 100)
String address;
#Column(name = "mobile", unique = false, nullable = false, length = 100)
String mobile;
#OneToOne
private Employee employee;
public int getAddId() {
return addId;
}
public void setAddId(int addId) {
this.addId = addId;
}
public int getPincode() {
return pincode;
}
public void setPincode(int pincode) {
this.pincode = pincode;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}
#Entity
#Table(name = "Employee")
public class Employee {
#Id
#Column(name = "empId", unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
int empId;
#Column(name = "empName", unique = false, nullable = false, length = 100)
String empName;
#Column(name = "empProfile", unique = false, nullable = false, length = 100)
String empProfile;
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name="addId")
Address address;
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpProfile() {
return empProfile;
}
public void setEmpProfile(String empProfile) {
this.empProfile = empProfile;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
<form:form cssClass="form-horizontal" method="POST" action="${pageContext.request.contextPath}/editEmployee" modelAttribute="employee">
<input type="hidden" value="${employeeDetail.empId}" name="empId">
<div class="form-group">
<label for="email">Employee Name:</label>
<input type="text" class="form-control" name="empName" value="${employeeDetail.empName}">
</div>
<div class="form-group">
<label for="pwd">Profile:</label>
<textarea rows="5" class="form-control" name="empProfile">${employeeDetail.empProfile}</textarea>
</div>
<div class="form-group">
<label for="pwd">Pincode:</label>
<input type="text" class="form-control" name="address.pincode" value="${employeeDetail.address.pincode}">
</div>
<div class="form-group">
<label for="pwd">Address:</label>
<textarea rows="5" class="form-control" name="address.address">${employeeDetail.address.address}</textarea>
</div>
<div class="form-group">
<label for="pwd">Mobile No:</label>
<input type="text" class="form-control" name="address.mobile" value="${employeeDetail.address.mobile}">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Update</button>
</div>
</form:form>
After execute following:
session=sessionFactory.getCurrentSession();
session.update(employee);
Result:
Hibernate: insert into Address (address, employee_empId, mobile, pincode) values (?, ?, ?, ?)
Hibernate: update Employee set addId=?, empName=?, empProfile=? where empId=?
Note: When update Employee why new row inserted in Address table. I want to update Employee and want to update Address table also rather than new row inserted.

Spring Boot, Thymeleaf, ManyToMany checkboxes evaluation

I've seen a lot of examples on the Internet and looks like the solution should work fine. But still could not make my code working.
User:
#Entity
#Table(name = "users")
public class User implements Serializable{
private static final long serialVersionUID = 1L;
...
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "user_usertypes", joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "usertype_id", referencedColumnName = "id"))
private Set<UserType> userTypes;
}
UserType:
#Entity
#Table(name = "usertypes")
public class UserType implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Version
#Column(name = "version")
private Integer version;
#Column(name = "name")
private String name;
#ManyToMany(mappedBy = "userTypes")
private Set<User> users;
#Override
public int hashCode() {
int hash = 5;
hash = 83 * hash + Objects.hashCode(this.id);
return hash;
}
#Override
public boolean equals(Object obj) {
System.out.println("comparing objects");
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()){
return false;
}
final UserType other = (UserType) obj;
return Objects.equals(this.id, other.id);
}
}
User Controller:
#Controller
public class UserController {
#RequestMapping(value = "/user", method = RequestMethod.POST)
public String saveUser(#Valid #ModelAttribute("user") User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "users/userform";
}
System.out.println(user.getUserTypes());
userService.saveUser(user);
return "redirect:/user/" + user.getId();
}
#InitBinder
private void initBinder(ServletRequestDataBinder binder) {
binder.registerCustomEditor(Set.class, "userTypes", new CustomCollectionEditor(Set.class) {
protected Object convertElement(Object element) {
if (element != null) {
System.out.println("From Controller: " + element.toString());
return userTypeService.findOne(Integer.parseInt(element.toString()));
}
return null;
}
});
}
userform:
<form th:object="${user}" th:action="#{/user}" method="post">
<input type="hidden" th:field="*{id}"/>
<ul>
<li th:each="type : ${types}">
<input type="checkbox" th:id="${type.id}" th:field="*{userTypes}" th:value="${type.id}"/>
<label th:for="${type.id}" th:text="${type.name}">name</label>
</li>
</ul>
<form>
The initBinder isn't called on submit. Only on page load.
So, my controller cannot get the userTypes objects. What is missing? Thank you!
I found an easy and quick solution. Probably, not the best one, but it works as expected. Hope, it will help someone.
User Entity:
private List<UserType> userTypes = new ArrayList<>();
In the controller, I created a helper that creates a new List for the current user to match the indexes on the form:
public String edit(#PathVariable Integer id, Model model) {
model.addAttribute("user", updatedTypes(userService.getUserById(id)));
model.addAttribute("types", userTypeService.getAllUserTypes());
return "users/userform";
}
private User updatedTypes(User user) {
List<UserType> userTypes = new ArrayList<>();
for (long i = 0; i < userTypeService.count(); i++) {
userTypes.add(new UserType());
}
for (UserType type : user.getUserTypes()) {
userTypes.add(type.getId() - 1, type);
}
user.setTypes(userTypes);
return user;
}
Template:
<li th:each="type, stat : ${types}">
<input type="checkbox" th:field="*{userTypes[__${stat.index}__]}"
th:value="${type.id}"/>
<label th:for="|userTypes${stat.index}|+1" th:text="${type.name}">
name
</label>
</li>
Also, I got rid of the initBinder method. I don't know why, but it absolutely useless.

Spring MVC + Hibernate Cannot get item property

I,m stuck. I have a problem with output of data. I try to make some kind of order-product project. My Entities are following:
#Entity
#Table(name = "sales")
public class Sale implements Serializable {
public Sale() {
}
#Id
#Column
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(insertable = false, updatable = false)
private Timestamp date;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "sale")
private List<OrderItem> items = new ArrayList<OrderItem>();
#Column
private double cost;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Timestamp getDate() {
return date;
}
public void setDate(Timestamp date) {
this.date = date;
}
public List<OrderItem> getItems() {
return items;
}
public void setItems(List<OrderItem> items) {
this.items = items;
}
public double getCost() {
return cost;
}
public void setCost(double cost) {
for(OrderItem item : items)
cost += item.getProduct().getPrice() * item.getQuantity();
this.cost = cost;
}
}
#Entity
#Table(name = "products")
public class Product implements Serializable {
public Product() {
}
#Id
#Column
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String name;
#Column
private double price;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "product")
private Set<OrderItem> items = new HashSet<OrderItem>();
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 double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Set<OrderItem> getItems() {
return items;
}
public void setItems(Set<OrderItem> items) {
this.items = items;
}
public boolean isNew() {
return this.id == 0;
}
}
#Entity
#Table(name = "order_items")
public class OrderItem implements Serializable {
#Id
#GeneratedValue
#Column(name = "id")
private Long id;
#Column
private int quantity;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "product_id")
private Product product;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "sale_id")
private Sale sale;
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
SQL tables create like this:
CREATE TABLE products (
id SERIAL PRIMARY KEY NOT NULL,
name CHARACTER(50) NOT NULL,
price REAL NOT NULL
)
WITH ( OIDS = FALSE );
CREATE TABLE sales (
id SERIAL PRIMARY KEY NOT NULL,
cost REAL NOT NULL,
date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
WITH ( OIDS = FALSE );
CREATE TABLE order_items (
id SERIAL NOT NULL,
sale_id INTEGER NOT NULL,
product_id INTEGER,
quantity INTEGER NOT NULL,
primary key (sale_id, id)
)
WITH ( OIDS = FALSE );
ALTER TABLE order_items
ADD CONSTRAINT order_itemsFK0 FOREIGN KEY (product_id) REFERENCES products(id);
ALTER TABLE order_items
ADD CONSTRAINT order_itemsFK1 FOREIGN KEY (sale_id) REFERENCES sales(id);
My sale form:
<form:hidden path="id" />
<spring:bind path="items">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Product</label>
<div class="col-sm-5">
<form:select path="items" class="form-control">
<form:options items="${productMap}" />
</form:select>
<form:errors path="items" class="control-label" />
</div>
<div class="col-sm-5"></div>
</div>
</spring:bind>
<spring:bind path="items">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Quantity</label>
<div class="col-sm-10">
<form:radiobuttons path="items" items="${numberList}" element="label class='radio-inline'" />
<br />
<form:errors path="items" class="control-label" />
</div>
</div>
</spring:bind>
<spring:bind path="cost">
<div class="form-group ${status.error ? 'has-error' : ''}">
<label class="col-sm-2 control-label">Cost</label>
<div class="col-sm-10">
<form:input path="cost" type="text" class="form-control" id="cost"
placeholder="Cost" />
<form:errors path="cost" class="control-label" />
</div>
</div>
</spring:bind>
And i have problems on form where I try to add sale. Items is incorrect, doesn`t save. i write jsp code wrong but i have no idea how to get it right. Need help, please!
Do you have some log? make sure that your post is arriving to the server side.
Solved. I remade some stuff in the project, in jsp part. Add additive page and everything works good.

Resources