Java Spring: how to validate only specific fields from my entity - java

I'm new in Spring and I have a problem with form validation. In my user edit form I want to validate only specific fields, not all fields annotated in my entity.
For example, if I have my UserEntity with fields:
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#Column(name = "user_id")
#GeneratedValue
public int user_id;
#NotEmpty
#Column(name = "userlogin")
public String userlogin;
#NotEmpty
#Column(name = "userpass")
public String userpass;
#NotEmpty
#Column(name = "name")
public String name;
#Email
#NotEmpty
#Column(name = "email")
public String email;
#NumberFormat(style = Style.NUMBER)
#NotEmpty
#Column(name = "phone")
public String phone;
and when I have register form, I need to validate all fields, and that's working fine.
But when I have edit user form, I want to edit and validate only 'name', 'email' and 'phone', I don't want to change 'userlogin' and 'userpass'.
But edit form won't pass successfully, because BindingResult validating all fields.
Here's my edit form:
<springForm:form action="/mywebapp/user/edit" commandName="user" method="POST">
<table>
<tr>
<td>Name:</td>
<td><springForm:input path="name" value="${user.name}" /></td>
<td><springForm:errors path="name" cssClass="error" /></td>
</tr>
<tr>
<td>E-mail:</td>
<td><springForm:input path="email" value="${user.email }" /></td>
<td><springForm:errors path="email" cssClass="error" /></td>
</tr>
<tr>
<td>Phone:</td>
<td><springForm:input path="phone" value="${user.phone}" /></td>
<td><springForm:errors path="phone" cssClass="error" /></td>
</tr>
<tr>
<td><input type="submit" value="Edit" /></td>
</tr>
</table>
</springForm:form>
Here is my controller method:
#RequestMapping(value="user/edit", method=RequestMethod.POST)
public String doUserEdit(#Valid #ModelAttribute("user") UserEntity user, BindingResult result, Principal principal) {
if(result.hasErrors()) {
return "user/edit";
}
UserEntity u = this.userService.getUser(principal.getName());
this.userService.editUser(u.getUser_id(), user);
return "redirect:/user";
}
result.hasError() always return true, because it validating also 'userlogin' and 'userpass'.
How to ignore other fields in edit form and validate only that fields that I want to?

I usually create a separate form class which is only suited for form submission processing and put all the necessary validation there:
public class UserUpdateForm {
#NotEmpty
private String name;
#Email
private String email;
#NotEmpty
#NumberFormat(style = Style.NUMBER)
private String phone;
//Getters and setters here
}
The idea is that you untie your model class from representations (form) classes. The only downside to that is that you'll have to handle transformations between the form objects and model objects somehow. Something like dozer might help though.

Related

Input data on form is being created but not presisting values that are being passed, how do i solve this?

I have a OneToMany mapping set up for my student to address, so a student may have multiple addresses. The items are mapped correctly, but when I persist data through the form, the id is generated for the address(user is already created I just have to click add address, also I am getting the correct user) but the values are null when I save, here are my java and jsp snippets
#PostMapping("/addAddress")
public String showFormForAddAddress(#RequestParam("studentId") int id, Model model) {
Student student = studentService.findStudentById(id);
Address address = new Address();
model.addAttribute("address", address);
List<Address> addresses = student.getAddresses();
addresses.add(address);
student.setAddresses(addresses);
studentService.saveStudent(student);
return "address-form";
}
#GetMapping("/addAddress")
public String addAddress(#RequestParam("studentId") int id, Model model) {
Address address = new Address();
address.setStudentId(id);
model.addAttribute("address", address);
return "address-form";
}
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Address Form</title>
</head>
<body>
<div id="wrapper">
<div id="header">
<h2>SRM Student Relationship Manager (Address)</h2>
</div>
</div>
<div id="container">
<h3>Save Address</h3>
<form:form action="addAddress" modelAttribute="address" method="POST">
<form:input path="studentId" />
<table>
<tbody>
<tr>
<td><label>Address One</label></td>
<td><form:input path="addressOne" /></td>
</tr>
<tr>
<td><label>City</label></td>
<td><form:input path="city" /></td>
</tr>
<tr>
<td><label>State</label></td>
<td><form:input path="state" /></td>
</tr>
<tr>
<td><label>Zip Code</label></td>
<td><form:input path="zipCode" /></td>
</tr>
<tr>
<td><label>Address Two</label></td>
<td><form:input path="addressTwo" /></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save"></td>
</tr>
</tbody>
</table>
</form:form>
</div>
</body>
</html>
Also for reassurance case these are my entities
#Entity
#Table(name = "address", catalog = "user_db")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "address_id")
private Integer id;
private String addressOne;
private String city;
private String state;
private String zipCode;
private String addressTwo;
#Column(name = "user_id")
private Integer studentId;
#Entity
#Table(name = "Student", catalog = "user_db")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "student_id")
private int id;
private String firstName;
private String lastName;
private String email;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "student_id", referencedColumnName = "student_id")
private List<Address> addresses;
I feel like this may be a naming issue but I cant quite put my finger on it, also I tried deleting the post and just using get, still didn't work.
this should be the showForForAddMethod
#PostMapping("/addAddress")
public String showFormForAddAddress(#RequestParam("studentId") int id, Model model,
#ModelAttribute("address") Address address) {
Student student = studentService.findStudentById(id);
model.addAttribute("address", address);
List<Address> addresses = student.getAddresses();
addresses.add(address);
student.setAddresses(addresses);
studentService.saveStudent(student);
return "redirect:/student/list";
}
If I create a new object the information is not mapped and hence it is lost, but when I pass address as a model attribute in the parameters, this points to the student object
In your showFormForAddAddress method, you wrote model.addAttribute("address", address); this puts values into model attribute.
But you were trying to get address information from this method, it is wrong.
You can get the related information by using the below approach:
Map<String,Object> modelMap = model.getModel().asMap();
Object object = modelMap.get("address");
After that you can convert the resulted object into Address type and use it

SpringBoot: parse button value to controller

im new in springboot and really need your expertise. Please help me.
i need to pass the data to controller when the button is click. Now im facing with the error below, what actually i do wrong in my code ?
'java.lang.String' to required type 'com.portal.dmtt.model.taskSchJob'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'TEST'; nested exception is java.lang.NumberFormatException: For input string: "TEST"
Controller
#RequestMapping(value="/runJob", method=RequestMethod.GET)
public String runJob(Model model, taskSchJob schJob) {
System.out.println("Start get request");
model.addAttribute("theTaskSchJobList", new taskSchJob());
schJob.getScript();
System.out.println("End get request");
return "redirect:/cronJob";
}
#RequestMapping(value="/runJob", method=RequestMethod.POST)
public String customerSubmit(#ModelAttribute taskSchJob schJob, Model model,#RequestParam String taskJobScript) {
System.out.println("Start post request");
System.out.println("End post request" + taskJobScript.toString());
return "redirect:/cronJob";
}
HTML
<tbody>
<tr th:each="taskJob : ${theTaskSchJobList}" style="text-align: center">
<form th:action="#{/runJob}" th:object="${theTaskSchJobList}" th:method="POST">
<td th:text="${taskJob.id}">Id</td>
<td th:text="${taskJob.title}">Username</td>
<td th:text="${taskJob.script}">Script</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<button type="submit" class="btn btn-success" th:value="${taskJob.script}" th:name="taskSchJob">
<span class="glyphicon glyphicon-play"></span>
</button>
</form>
</td>
</tr>
</tbody>
Model
#Entity
#Table (name = "task_Sch_Job")
public class taskSchJob {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "title")
private String title;
#Column(name = "username")
private String username;
#Column(name = "script")
private String script;
#Column(name = "date_create")
private String date_create;
#Column(name = "cron_job")
private String cron_job;
#Column(name = "status")
private String status;
//------
//setter and getter
//------
the below image is, when the user click the button, it will send the data 'title' to the controller.
Your custom class com.portal.dmtt.model.taskSchJob isn't String. Please consider passing the valid JSON/XML in request body. This is applied to model object as well.
finally, i did it.Please find the below answer:
#RequestMapping(value = "/taskRun/{id}", method = RequestMethod.GET, params = "actionEdit")
public String taskEdit(Model model, #PathVariable(required = true, name = "id") Long id) {
System.out.println("Start GET request: the id is: " + id);
return "redirect:/cronJob";
}
HTML text by using GET Method
<form th:action="#{/taskRun/__${taskJob.id}__}">
<button type="submit" class="btn btn-success" th:name="actionRun" th:value="run">
<span class="glyphicon glyphicon-play"></span>
</button></form>

Save input field in Thymeleaf to Set<>

I would like to save the inputs to the Set interface. I have class Client.java:
#Table(name = "client")
public class Client {
#OneToOne(cascade = CascadeType.ALL)
private ShippingAddress shippingAddress;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Address> shippingAddresses = new HashSet<>();
}
Class ShippingAddress.java:
#Table(name = "address")
public class ShippingAddress {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
Long id;
String street;
String zip;
}
And it's my create form:
<form th:action="#{/add}" method="post" th:object="${client}">
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress.street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress.zip}"/></td>
</tr>
It works fine, but I can only save one street and one zip. I tried to improve it in this way to be able to save more data:
<form th:action="#{/add}" method="post" th:object="${client}">
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress[0].street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress[0].zip}"/></td>
</tr>
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress[1].street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress[1].zip}"/></td>
</tr>
But I get information:
Invalid property 'shippingAddress[0]' of bean class [model.Client]: Property referenced in indexed property path 'shippingAddress[0]' is neither an array nor a List nor a Set nor a Map; returned value was [ShippingAddress(id=null, street=null, zip=null, state=null, city=null, country=null)]
To add values to Set I should use the add method? But how to implement it with Thymeleaf?
Method in Controller (save data):
#Transactional
#RequestMapping(value = "add", method = RequestMethod.POST)
public String saveClient(#ModelAttribute Client client) {
clientRepository.save(client);
return "redirect:/";
}
Method in Controller (open create form)
#RequestMapping("/create")
public String newClient(Model model) {
model.addAttribute("client", new Client());
return "create";
}

Spring mvc handling differences in types between form and model

I have this form
<form:form action="saveCustomer" modelAttribute="customer" enctype="multipart/form-data" method="POST">
<!-- need to associate this data with customer id -->
<form:hidden path="id" />
<table>
<tbody>
<tr>
<td><label>First name:</label></td>
<td><form:input path="firstName" /></td>
</tr>
<tr>
<td><label>Last name:</label></td>
<td><form:input path="lastName" /></td>
</tr>
<tr>
<td><label>Email:</label></td>
<td><form:input path="email" /></td>
</tr>
<tr>
<td><label>Profile Image:</label></td>
<td>
<form:input type="file" path="file" id="file" class="form-control input-sm"/>
</td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save" class="save" /></td>
</tr>
</tbody>
</table>
</form:form>
and this model
#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;
#NotEmpty
#Column(name="file")
private String file;
}
In my model, I have decided not to define the file field as MultipartFile and instead I went with String.
I did that to enable me just grab the uploaded files file name and leave spring mvc to upload the file. That works but when I introduce error checking I get this error:
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'customer' on field 'file': rejected value
[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile#2a8400bb];
codes
[typeMismatch.customer.file,typeMismatch.file,typeMismatch.java.lang.String,typeMismatch];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [customer.file,file]; arguments []; default message [file]];
default message [Failed to convert property value of type
[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile]
to required type [java.lang.String] for property 'file'; nested
exception is java.lang.IllegalStateException: Cannot convert value of
type
[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile]
to required type [java.lang.String] for property 'file': no matching
editors or conversion strategy found]
My controller:
#RequestMapping(value = "/saveCustomer", method = RequestMethod.POST)
public String saveCustomer(#Valid FileBucket fileBucket,
ModelMap model, #ModelAttribute("customer") Customer theCustomer,BindingResult result) throws IOException {
if (result.hasErrors()) {
System.out.println("validation errors");
return "customer-form";
} else {
System.out.println("Fetching file");
MultipartFile multipartFile = fileBucket.getFile();
// Now do something with file...
FileCopyUtils.copy(fileBucket.getFile().getBytes(), new File( UPLOAD_LOCATION + fileBucket.getFile().getOriginalFilename()));
String fileName = multipartFile.getOriginalFilename();
model.addAttribute("fileName", fileName);
theCustomer.setFile(fileName);
customerService.saveCustomer(theCustomer);
return "redirect:/customer/list";
}
}
How can I handle this error?
In your error part say about this only
typeMismatch.customer.file,typeMismatch.file,typeMismatch.java.lang.String,typeMismatch
but in entity class file is a string
<form:form action="saveCustomer" modelAttribute="customer" enctype="multipart/form-data" method="POST">
In your form
<form:input type="file" path="file" id="file" class="form-control input-sm"/>
but, here your modelAttribute is customer front end its and file input and backend it's a String so that your having problem
private String file is incorrect param because it's a multipart data so you should use MultipartFile in your entity class
#Entity
#Table(name="customer")
public class Customer {
... . . . . ..
private MultipartFile file;
//getters setters
}
I created another field called path and made file transient
#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;
#NotEmpty
#Column(name="path")
private String path;
#Transient
private MultipartFile file;
and that worked.

Spring web mvc Collection in entity class

Lets say i have 2 entities, Dish and Ingrediƫnt.
As we all know a Dish consists out of multiple Ingredients, so let's say:
Dish.java:
#Entity
public class Dish {
#Id
#GeneratedValue
protected long id;
private String name;
#OneToMany(mappedBy = "dish", cascade = CascadeType.PERSIST)
private Collection<Ingredient> ingredients;
//getters & setters
}
Ingrediƫnt.java :
#Entity
public class Ingredient {
#Id
#GeneratedValue
protected long id;
private String name;
//getters &setters
}
If we then have a DishController.java :
#Controller
public class DishController {
Service service;
public DishController() throws ServiceException {
service = new ShoppingFacade("JPA");
}
#RequestMapping("/showDishOverview")
protected ModelAndView getDishes() throws ServiceException {
Collection<Dish> dishes = service.getAllDishes();
return new ModelAndView("dish/dishOverview", "dishes", dishes);
}
#RequestMapping(value = "/showDishForm", method = RequestMethod.GET)
protected ModelAndView showDishForm(#RequestParam(value = "id") long dishId)throws ServiceException{
Dish dish = shoppingFacade.getDishById(dishId);
return new ModelAndView("dish/dishForm", "dish", dish);
}
#RequestMapping(value = "/editDish", method = RequestMethod.POST)
protected String updateDish(#ModelAttribute("dish") Dish newDish) throws ServiceException{
service.updateDish(newDish);
return "forward:/showDishOverview.htm";
}
}
Now lets say that we select one dish by its id:
${dish.getName()}
Now the showDishForm method in our controller will be called.
which will send us to dishForm.jsp:
<form method="post" action="editDish.htm">
<table>
<tr>
<td><label>Name </label></td>
<td><input type="text" name="name" placeholder="${dish.getName()}" value="${dish.getName()}"/></td>
</tr>
<tr>
<td><label>People</label></td>
<td><input type="text" name="people" placeholder="${dish.getPeople()}" value="${dish.getPeople()}"/></td>
</tr>
<tr>
<td colspan="2">
<input type="hidden" name="ingredients" value="${dish.getIngredient()}" />
<button type="submit" name="id" value="${dish.getId()}">Save</button>
</td>
</tr>
</table>
</form>
How will Spring MVC pass the info from my view to the controller?
Cause I get it working as long as I don't pass any Collections.(comment out <input type="hidden" name="ingredients" value="${dish.getIngredient()}" />
)
If i try to pass a Collection i get the message :
HTTP STATUS 400 - The request sent by the client was syntactically
incorrect
Spring uses Converters, for all standard classes these are already implemented.
For custon classes like this List, you have to write your own converter.

Categories