I'm trying to use a validation group in Spring, but it doesn't seem to want to work.
When using #Valid User member, it works fine. When I used #Validated(FormValidationGroup.class), it doesn't. This is the method signature.
Code:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String doRegister(Model model, #Valid User member, BindingResult result) {
logger.info("Showing Registration Page....");
if (result.hasErrors()) {
return "createmembers";
}
if (userService.exists(member.getUsername())) {
result.rejectValue("username", "Duplicate Key",
"This email address has already been used");
return "createmembers";
}
else {
userService.create(member);
return "registerSuccess";
}
}
Other method
Code:
#RequestMapping("/createmembers")
public String createMembers(Model model) {
logger.info("Showing Create Members Page....");
model.addAttribute("member", new User());
return "createmembers";
}
The part I've highlighted is all that changes.
The relevant error from the trace seems to be
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'member' available as request attribute
The JSP page is as follows
Code:
<sf:form id="details" method="post" action="${pageContext.request.contextPath}/register" commandName="member">
<table class="formtable">
<tr><td>Name</td><td><sf:input name = "name" path="name" type="text"/><br/><sf:errors path="name" cssClass="error"></sf:errors></td></tr>
<tr><td>Password</td><td><sf:input id="password" name = "password" path="password" type="password"/><br/><sf:errors path="password" cssClass="error"></sf:errors></td></tr>
<tr><td>Confirm Password</td><td><input id = "con_password" name = "con_password" type="password"/><div id="matchpass"></div></td></tr>
<tr><td>Email</td><td><sf:input name = "username" path="username" type="text"/><br/><sf:errors path="username" cssClass="error"></sf:errors></td></tr>
<tr><td>Gender</td><td><sf:select name="gender" path="gender" class="select"><option value = "M">Male</option><option value = "F">Female</option></sf:select></td></tr>
<tr><td>Membership Type</td><td><sf:select name="member_type" path="member_type"><option value = "Senior">Senior</option><option value = "Junior">Junior</option><option value = "Student">Student</option></sf:select></td></tr>
<tr><td>Grade</td><td><sf:select name="grade" path="grade"><option value = "Graded">Graded</option><option value = "Intermediate">Intermediate</option><option value = "Beginner">Beginner</option></sf:select></td></tr>
<tr><td>Address Line 1</td><td><sf:input name = "ad_line1" path="ad_line1" type="text"/><br/><sf:errors path="ad_line1" cssClass="error"></sf:errors></td></tr>
<tr><td>Address Line 2</td><td><sf:input name = "ad_line2" path="ad_line2" type="text"/><br/><sf:errors path="ad_line2" cssClass="error"></sf:errors></td></tr>
<tr><td>City/Town</td><td><sf:input name = "ad_city" path="ad_city" type="text"/><br/><sf:errors path="ad_city" cssClass="error"></sf:errors></td></tr>
<tr><td>County</td><td><sf:input name = "ad_county" path="ad_county" type="text"/><br/><sf:errors path="ad_county" cssClass="error"></sf:errors></td></tr>
<tr><td>Contact Number</td><td><sf:input name = "contact_num" path="contact_num" type="text"/><br/><sf:errors path="contact_num" cssClass="error"></sf:errors></td></tr>
<tr><td>Emergency Contact Name</td><td><sf:input name = "em_con_name" path="em_con_name" type="text"/><br/><sf:errors path="em_con_name" cssClass="error"></sf:errors></td></tr>
<tr><td>Emergency Contact Number</td><td><sf:input name = "em_con_num" path="em_con_num" type="text"/><br/><sf:errors path="em_con_num" cssClass="error"></sf:errors></td></tr>
<tr><td><input value="Register" type="submit"/></td></tr>
</table>
</sf:form>
It's only a partial page as I'm using Apache Tiles, and I've left out the script for checking the password.
As I said, this works fine with the #Valid annotation, but since I'm switching from jdbc to hibernate, I'm losing validation. I want the password field to be between 5-15 for users, but since I am encrypting them, Hibernate won't let them pass validation when writing them to the database.
My understanding in the user clicks on Register, this maps to the createmembers.jsp (which is actually Apache tiles using header.jsp, footer.jsp and createmembers.jsp) - this jsp contains a form which connects to the register mapping in my controller and adds the user object to the model. The register method then reads that objects, does validation and if it's all good, writes to database. If bad, it goes back to the createmembers page, otherwise registerSuccess page. Like I said, I did have Validation working with the#Valid annotation and with JDBC, but with both Spring and Hibernate validating, I need to fix it up a bit.
Fixed it. I had to explicitly annotate the user as a model attribute. Here's the new method signature.
public String doRegister(Model model, #Validated(FormValidationGroup.class) #ModelAttribute("member") User member, BindingResult result)
Related
Return List of objects
#RequestMapping("/mvt")
public String showMvt(Model model){
model.addAttribute("counts", mouvementRepository.findAll());
return"mvt_";
}
Update one line controller
#RequestMapping(value="/updateMvt/{idmvt}",method = {RequestMethod.GET})
public String updateMvt(#PathVariable(value = "idmvt") int idcpn, #Valid #ModelAttribute("mvt") Mouvement mvt,BindingResult result) {
mouvementRepository.save(mvt);
return "redirect:/mvt";
}
I tried To edit lanes inside tr th:each like this
<tr th:each="count:${counts}">
<form action="#" th:action="#{/updateMvt/{idmvt}(idmvt=${count.idmvt})}" th:object="${count}" method="put">
<table border="0" cellpadding="10">
<tr>
<td>etat</td>
<td>
<select class="select-css" th:field="*{count.etat}">
<option th:value="Enregistrement">Enregistrement</option>
<option th:value="Embarquement">Embarquement</option>
<option th:value="Decollé">Decollé</option>
<option th:value="Annulé" selected>Annulé</option>
<option th:value="null" selected>Aucun</option>
</select>
</td>
</tr>
</table>
</form>
I'm new to spring,i know this solution looks bad,but i'm trying to do my best.
so I tried to do it this way, but I get the error :
Neither BindingResult nor plain target object for bean name 'count'
available as request attribute
Update --------------------------------------------------------------------
so far I managed to create a wrapper class Like this
public class mvtForm {
private int version;
private List<Mouvement> mouvements;//Getters and setters are included
And in my controller I set the movements attribute like this :
#ModelAttribute("wrapper")
#RequestMapping(value = "/mvtall")
public String processQuery(#ModelAttribute mvtForm wrapper, Model model) {
wrapper.setMouvements(mouvementRepository.findAll());
model.addAttribute("wrapper",wrapper);
return "mvt_all";
}
Now I got the view that I needed,
but Is there a way to submit PUT to each lane using JpaRepository ?
th:object must reference an object from Model, not from iteration. So you need to pass count object to model in controller before loading the page, like for example by defining this method in your controller:
#ModelAttribute("updateCount")
public Count count() {
return new Count();
}
class Count should have a field int version that will be unique for each Count object, this is important for updating. Don't forget getters, setters and default constructor - this is important for Thymeleaf.
#ModelAttribute("listOfCounts")
public List<Count> listOfCounts() {
return listOfCounts; //has to be predefined somewhere in class
//as it will be reused throughout application.
}
When you iterate you will be iterating over a listOfCounts. However when updating and pressing update button you will be binding the updateCount object. Make sure to have a hidden field with version in thymeleaf. With this when receiving the update request in your controller, you will receive the updateCount object with the correct version, you can find the correct Count object from the listOfCounts via version and update it.
Basically my question is very simple, I have a registration form with a constraint on field "login".
Table(uniqueConstraints=#UniqueConstraint(columnNames = { "login" }))
So, when I add a login which is already exists, an exception shows up: Duplicate entry 'MyLogin' for key 'UKew1hvam8uwaknuaellwhqchhb'.
I'm asking if there's any way in thymeleaf to just show a message saying that name already exists.
I would check if user already exists in table with the same username(I think your "login" is same as username for the user) before making any persistence transaction for new user, if username already exists in the table simply do not go forward and execute new user registration logic and let form controller return view with the Model object with the duplicate username attribute returned by service method.(you can use exceptions in service methods for better logic)
#RequestMapping("/registerNewUser")
public String showModel(#ModelAttribute UserDataTransferObject userDTO, Model model){
String existedUsername = service.createUser(userDTO);
if(existedUsername != null){
model.addAttribute("existedUsername",existedUsername);
}
return "registrationstatus";
}
createUser method checks if repository contains entry with the same username and if so returns that username as a string (Simple implementation).
in thymeleaf:
<div th:if="${existedUsername != null}" class="alert alert-danger notification" role="alert">
<p th:text="${existedUsername}"></p><p> already exists</p>
</div>
i want to create a form in which user can set name, age, birthday and department which is another object..
in jsp i already have the form with the inputs of name, age and birthday. For department i already create the select/option tag with the results from hibernate/db.
The problem is when i sumbit and post the the mployee i have a lot of erros
#RequestMapping(value = { "/new" }, method = RequestMethod.POST)
public String saveEmployee(#Valid #ModelAttribute Employee employee,
#PathVariable("department") int department, BindingResult result,
ModelMap model) {
jsp file
<tr>
<td>
<select name="department">
<c:forEach var="dep" items="${departments}">
<option value="${dep.id}">${dep.name}</option>
</c:forEach>
</select>
</td>
</tr>
i want to pass with post the employee info with department id to create the employee
Your BindingResult parameter needs to go directly after your Employee parameter.
I have an administration panel where i can add users.
In insert form i have USERNAME, PASSWORD (no confirm-password), and some ordinary fields like name, surname,....
When i insert a new user, i want that hibernate will validate it:
username not null
unique username in db (with an unique index on table)
password not null (for now, no length controls)
until here, everything is working fine.
As in future i will store encrypted passwords,
in Edit form, i have normal editable fields like username, name,
but i didn't put password. instead i put new password field just in case i want to change it (not mandatory).
my edit form:
<!--
- username
-->
<div class="form-group">
<label class="col-sm-4 control-label">Username*:</label>
<div class="col-sm-8" ><input class="form-control" type="text" data-th-field="*{username}" placeholder="Username" th:errorclass="'has-error-input'"></input></div>
</div>
<!--
- password
- fingo password
-->
<div class="form-group">
<label class="col-sm-4 control-label">Password:</label>
<div class="col-sm-8" >******</div>
</div>
<!--
- new password
- (if i want to change it)
-->
<div class="form-group">
<label class="col-sm-4 control-label">New Password:</label>
<div class="col-sm-8" ><input class="form-control" type="password" id="password_new" name="password_new" placeholder="New Password"></input></div>
</div>
As you can see the password_new field is a normal tag, without using spring notation. I will retrieve this as parameter in controller and i will check if new password was written or no.
Now the problem: when i submit the form, i get validation error, as password is null.
I'm asking you this:
am i on a good way to do this?
is it possible skip a validation (password) in hibernate on update,
but set as mandatory on insert? and then update all fields except
password (you will see that i commented the line
userToUpdate.setPassword(user.getPassword());)?
could be better remove new password field from edit form, and change
password in a page where i update only the password?
is it a stupid / unsafe idea to set an hidden field called 'password' in edit form containing the encrypted password, so it will not give me validation errors? thinking for a second, i think it's really not a good idea, as someone can see encrypted password just looking the source code of the page. having a encrypted password "in clear" can be dangerous / unsafe?
User.java (model)
#NotNull(message = "PASSWORD cannot be null")
#NotEmpty(message = "PASSWORD cannot be null")
#Size(max = 50, message = "Max 50 char")
#Column(name = "password", length = 50)
private String password;
UserDao.java
#Override
public void updateUser(User user) throws UserNotFoundException {
User userToUpdate = getUser(user.getId());
userToUpdate.setUsername(user.getUsername());
//userToUpdate.setPassword(user.getPassword());
userToUpdate.setNome(user.getNome());
userToUpdate.setCognome(user.getCognome());
userToUpdate.setEmail(user.getEmail());
userToUpdate.setEnabled(user.getEnabled());
userToUpdate.setRole(user.getRole());
getCurrentSession().update(userToUpdate);
}
UserController.java
#RequestMapping(value = "/edit", method = RequestMethod.POST)
public String editingUser(#Valid #ModelAttribute User user,
BindingResult result, RedirectAttributes redirectAttrs,
#RequestParam(value = "action", required = true) String action,
#RequestParam(value = "password_new") String password_new
){
logger.info("IN: User/edit-POST: " + action);
List<Role> user_roles = RoleService.getRoles();
if (action.equals("abort"))
{
/**
* message
*/
String message = "Edit not saved";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-warning");
redirectAttrs.addFlashAttribute("user_roles", user_roles);
}
else if (result.hasErrors())
{
logger.info("User-edit error: " + result.toString());
redirectAttrs.addFlashAttribute("org.springframework.validation.BindingResult.user", result);
redirectAttrs.addFlashAttribute("user", user);
/**
* as in my list page i have also the insert form,
* i need to populate the select
*/
redirectAttrs.addFlashAttribute("user_roles", user_roles);
return "redirect:/users/edit?id=" + user.getId();
}
else if (action.equals("save"))
{
try
{
logger.info("User/edit-POST: " + user.toString());
/**
* i see if i changed the password
*/
logger.info("new password: " + password_new);
if (!password_new.equals(""))
{
user.setPassword(password_new);
}
else
{
//nothing, or what?
}
/**
* now i can update the user in DB
*/
UserService.updateUser(user);
/**
* message
*/
String message = "User edited with success";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-success");
/**
* as in my list page i have also the insert form,
* i need to populate the select
*/
redirectAttrs.addFlashAttribute("user_roles", user_roles);
}
catch (UserNotFoundException e)
{
logger.info("User/edit-POST: id [" + user.getId() + "] not found");
String message = "User id [" + user.getId() + "] not found!";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-danger");
}
}
return "redirect:/users/list";
}
thank you very much
PS.
As it's my first application in spring / hibernate / java word, for now i'm storing plain passwords, in future i will encrypt them.
Sure!
Only if you remove the #Valid annotation. What you could do is to remove the annotation, then set all properties in the controller and before calling the DAO function validate the entity manually:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
It's up to you.
Yes, don't do that :)
•username not null
You can use #NotNull in model.
•unique username in db (with an unique index on table)
instead of doing at the time of inserting you can make an AJAX call when username field on form get out of focus to check entered user is exit or not.
While updating you want to update fields without updating password (if its blank)
First of all I would like to tell you that NotEmpty and NotNull annotation from domain model. I don't use the #NotNull and #NotEmpty validations in practice you can refer this article
You can use hibernates dynamic-update to update only those properties which is changed. Go Through this
You can use password field on the edit form and as per your idea you can validate if user enters something in password field then update is required other wise first fetch the object from DB set to updating object then call for hibernate to update the object.
Better Way
To change your models As you said you want to take users FirstName,LastName,other personal details that should be in Person model and all the login account related part in LoginAccount model
So your model becomes like :
Person
PersonId
FirstName
LastName
etc.
LoginAccount
LoginAccountId
UserName
Password
FK_PersonID
And you make viewmodel for new registration form and change password and edit profile functionalities to update details.
I have the following requirement. I submit a Model object to a view as follows...
#RequestMapping(value ="/addItem", method = RequestMethod.GET)
public ModelAndView showContacts() {
ModelAndView modelAndView = new ModelAndView("addItem", "command", new Item());
return modelAndView;
}
But on post, I need to retrieve a value apart from the "Item" object (model) that is returned to me. I can't have this variable be a part of the Item model object because it does not belong there. But I need it returned in order to act on that value. How can I get about doing this ?
I.e. In my JSP file, I have the following fields...
<form:input type="text" path="val1"/>
<form:input type="text" path="val2"/>
<form:input type="text" path="val3"/>
Out of the above, only fields val1 and val2 have mappings to the Item object, where as val3 does not. Nevertheless, I need the value of val3 passed back to my controller as well. The code I have right now to handle the POST is as follows, but I can't figure out how to get the value for val3. The code does not compile right now because it says that there is no field or appropriate getter method in the Item class for val3.
#RequestMapping(value = "/postItem", method = RequestMethod.POST)
public String postItem(#ModelAttribute("item") Item item , BindingResult result) {
logger.info("Post Item:");
return "home";
}
How can I modify the above code to suite my requirement ?
Some guidance on this matter will be highly appreciated.
You can pass a map as the model, and include all sorts of different things in there. You are not limited to a single domain object, that constructor is there as a convenience, it is not the only way to do it. There is a variation of the constructor:
public ModelAndView(Object view,
Map model)
Create a new ModelAndView given a View object and a model.
Parameters:
view - View object to render (usually a Servlet MVC View object)
model - Map of model names (Strings) to model objects (Objects).
Model entries may not be null, but the model Map may be null if
there is no model data.