I would like to build a form to save an User. I have 2 tables, User and UserRole as described in Spring Security. To save an user I need to create a User Object that contains a field Set. I want to create a list of checkboxes and map them into an Set of UserRoles, but I don't know how to map them with checkboxes. I have the following classes:
#Controller
public class UserController {
#Autowired
IUserService userService;
/** Default GET form handler for users, in submission will call saveRegistration */
#RequestMapping(value="/createuser", method=RequestMethod.GET)
public String createUser(Model model) {
// User form will be bind to this User object
model.addAttribute("user", new User());
// Code about adding the user roles to JSP?
// Maybe something like this?:
// User u = new User ("useruser","123456",false);
// Set<UserRole> roles = new HashSet<UserRole>();
// roles.add(new UserRole(u,"ROLE_ADMIN"));
// roles.add(new UserRole(u,"ROLE_USER"));
// model.addAttribute("roles", roles);
return "createuser";
}
/** This method will be called on form submission, handling POST request,
* It also validates the user input */
#RequestMapping(value="/createuser", method=RequestMethod.POST)
public String doCreateUser(Model model, #Valid User user, BindingResult result) {
if(result.hasErrors()) {
return "createuser";
}
userService.createUser(user,user.getUserRole()) //createUser(User user, Set<UserRole> role)
return "success";
}
}
My JSP:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link rel="stylesheet" type="text/css" href="resources/css/createuser.css" />
</head>
<body onload='document.createUserForm.username.focus();'>
<sf:form name="createUserForm" method="post"
action="${pageContext.request.contextPath}/createuser"
commandName="user">
<table class="formtable">
<tr>
<td class="label">Username:</td>
<td><sf:input class="control" path="username" name="username"
type="text" /><br />
<div class="error">
<sf:errors path="username"></sf:errors>
</div></td>
</tr>
<tr>
<td class="label">Role:</td>
<td>
<ul>
<sf:checkboxes element="li" path="userRole" items="${roles}"></sf:checkboxes>
</ul></td>
</tr>
<tr>
<td class="label">Password:</td>
<td><sf:input class="control" path="password" name="password"
type="password" />
<div class="error">
<sf:errors path="password"></sf:errors>
</div></td>
</tr>
<tr>
<td class="label">Confirm Password:</td>
<td><input class="control" name="confirmpass" type="password" />
<div class="error">
<sf:errors path="password"></sf:errors>
</div></td>
</tr>
<tr>
<td class="label"></td>
<td><input class="control" value="Create account" type="submit" /></td>
</tr>
</table>
</sf:form>
User Role:
public class UserRole {
private Integer userRoleId;
private User user;
private String role;
public UserRole () {
}
public UserRole(User user, String role) {
this.user = user;
this.role = role;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_role_id",
unique = true, nullable = false)
public Integer getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(Integer userRoleId) {
this.userRoleId = userRoleId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "username", nullable = false)
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Column(name = "role", nullable = false, length = 45)
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String toString () {
return role;
}
}
And User:
public class User {
#NotNull
#NotBlank(message="Username cannot be blank.")
#Size(min=4, max=15, message="Username must be between 4 and 15 characters long.")
#Pattern(regexp="^\\w{6,}$", message="Username can only consist of numbers, letters and the underscore character.")
private String username;
#NotBlank(message="Password cannot be blank.")
#Pattern(regexp="^\\S+$", message="Password cannot contain spaces.")
#Size(min=6, message="Username must be longer than 6 characters.")
private String password;
//private String confirmPassword;
private boolean enabled;
private Set<UserRole> userRole = new HashSet<UserRole>(0);
public User() {
}
public User(String username, String password, boolean enabled) {
this.username = username;
this.password = password;
this.enabled = enabled;
}
public User(String username, String password,
boolean enabled, Set<UserRole> userRole) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.userRole = userRole;
}
#Id
#Column(name = "username", unique = true, nullable = false, length = 45)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "password", nullable = false, length = 60)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(name = "enabled", nullable = false)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
public Set<UserRole> getUserRole() {
return userRole;
}
public void setUserRole(Set<UserRole> userRole) {
this.userRole = userRole;
}
/*public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}*/
}
Thanks in advance.
Refert this post, and i dont see the itemlabel and itemid attribute, which maps to userrole object.
also refer post
Found solution using Properties Editor. Here and here
Related
I have two close to each other controllers - one for products and one for users.
I'd like to add Spring Validation to both controllers.
So I added ProductValidator and UserValidator classes which implement Validator.
ProductValidator works fine.
The problem is UserValidator is never called.
InitBinder method is being called, but validation doesn't work.
At the same time everything works fine with products in ProductController.
Here's the code of user logic.
UserController
#Controller
#RequestMapping(path = "/users")
public class UserController {
private final UserService userService;
private final RoleService roleService;
private PasswordEncoder passwordEncoder;
#Autowired
public UserController(UserService userService, RoleService roleService, PasswordEncoder passwordEncoder) {
this.userService = userService;
this.roleService = roleService;
this.passwordEncoder = passwordEncoder;
}
#Autowired
#Qualifier("userValidator")
private UserValidator userValidator;
#InitBinder("user")
private void initBinder(WebDataBinder binder) {
binder.setValidator(userValidator);
binder.registerCustomEditor(Role.class, "roles", new RoleEditor(roleService));
}
...
#RequestMapping(path = "/save", method = RequestMethod.POST)
public ModelAndView submit(#ModelAttribute("user") #Valid User user,
BindingResult result) {
...
}
...
}
UserValidator
#Component("userValidator")
public class UserValidator implements Validator {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
#Autowired
public UserValidator(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
#Override
public boolean supports(Class<?> paramClass) {
return User.class.equals(paramClass);
}
#Override
public void validate(Object obj, Errors errors) {
User user = (User) obj;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "username.required", "Enter username");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "firstName.required", "Enter user first name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "lastName.required", "Enter user last name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "password.required", "Enter user password");
User userToFind = userRepository.findByUsername(user.getUsername()).orElse(new User());
if (Objects.isNull(user.getId()) && Objects.nonNull(userToFind.getId())) {
errors.rejectValue("username", "username.already.exist", "user with this username already exists");
}
if (Objects.isNull(user.getRoles())) {
errors.rejectValue("roles", "role.required", "role must be assigned");
}
Role adminRole = roleRepository.getAdminRole();
if (Objects.nonNull(user.getId())
&& Objects.nonNull(user.getRoles())
&& !user.getRoles().contains(adminRole)
&& userRepository.findUsersWithAdministratorRole().size() == 1) {
errors.rejectValue("roles", "admin.required", "at least one admin role must exist");
}
}
public ErrorMessage validateUserToDelete(UUID id) {
ErrorMessage errorMessage = new ErrorMessage();
List<String> errors = new ArrayList<>();
Set<User> admins = userRepository.findUsersWithAdministratorRole();
if (admins.size() == 1) {
errors.add(String.format("User with email %s is the one with Admin role. Impossible to delete last Admin user."
, userRepository.findById(id).get().getUsername()));
}
errorMessage.setErrors(errors);
return errorMessage;
}
}
User
#Entity
#Table(name = "users")
public class User {
private UUID id;
private String username;
private String password;
private String firstName;
private String lastName;
private BigDecimal money;
private Set<Role> roles;
private Set<Product> products;
#Id
#Type(type="org.hibernate.type.PostgresUUIDType")
#Column(name = "id", columnDefinition = "uuid")
#GeneratedValue(strategy = GenerationType.AUTO)
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
#Column(name = "username")
#NotEmpty
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "password")
#NotEmpty
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(name = "first_name")
#NotEmpty
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#Column(name = "last_name")
#NotEmpty
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(name = "money")
#DecimalMin(value = "0.0", inclusive = false)
#Digits(integer = 10, fraction = 2)
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
#JoinTable(
name = "users_roles",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") }
)
#NotNull
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
#JoinTable(
name = "checkout",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "product_id") }
)
public Set<Product> getProducts() {
return products;
}
public void setProducts(Set<Product> products) {
this.products = products;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id.equals(user.id) && username.equals(user.username) && password.equals(user.password) && firstName.equals(user.firstName) && lastName.equals(user.lastName);
}
#Override
public int hashCode() {
return Objects.hash(id, username, password, firstName, lastName);
}
}
user.jsp
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<!DOCTYPE html>
<html>
<head>
<c:import url="${contextPath}/WEB-INF/jsp/header.jsp"/>
</head>
<body>
<c:import url="${contextPath}/WEB-INF/jsp/navibar.jsp"/>
<div class="container">
<div class="row">
<security:authorize access="hasRole('ROLE_ADMIN')">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group me-2" role="group" aria-label="Second group">
Back to users
</div>
</div>
</security:authorize>
</div><br>
<form:form action="/users/save" method="post" modelAttribute="user">
<div class="form-group">
<div class="row">
<form:label path="id">User ID:</form:label><br>
<form:input path="id" type="UUID" readonly="true" class="form-control" id="id" placeholder="User ID" name="id" value="${user.id}"/>
<br>
<form:label path="username">username (email address):</form:label><br>
<form:input path="username" type="text" class="form-control" id="username" placeholder="Enter username" name="username" value="${user.username}"/>
<form:errors path="username" cssClass="error"/>
<br>
<form:label path="password">Password:</form:label><br>
<form:input path="password" type="text" class="form-control" id="password" placeholder="Enter password" name="password" value="${user.password}"/>
<form:errors path="password" cssClass="error"/>
<br>
<form:label path="firstName">First name:</form:label><br>
<form:input path="firstName" type="text" class="form-control" id="firstName" placeholder="Enter first name" name="firstName" value="${user.firstName}"/>
<form:errors path="firstName" cssClass="error"/>
<br>
<form:label path="lastName">First name:</form:label><br>
<form:input path="lastName" type="text" class="form-control" id="lastName" placeholder="Enter last name" name="lastName" value="${user.lastName}"/>
<form:errors path="lastName" cssClass="error"/>
<br>
<form:label path="money">Money:</form:label><br>
<form:input path="money" type="number" class="form-control" id="money" placeholder="Enter money" name="money" value="${user.money}"/>
<form:errors path="money" cssClass="error"/>
<br>
<security:authorize access="hasRole('ROLE_ADMIN')">
<form:label path="roles">Roles:</form:label><br>
<c:forEach items="${roles}" var="role">
<form:checkbox path="roles" id="${roles}" label="${role.name}" value="${role}"/></td>
</c:forEach>
<br>
<form:errors path="roles" cssClass="error"/><br><br>
</security:authorize>
</div>
<div class="row">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group me-2" role="group" aria-label="Second group">
<form:button type="submit" value="Submit" class="btn btn-primary">Save</form:button>
</div>
</div>
</div>
</div>
</form:form>
</div>
</body>
</html>
If you look at the classname from your debugger com.intellias.testmarketplace.controller.UserController$$EnhancerBySpringCGLIB$$309171b7 you see the $$EnhancerBySpringCGLIB. That part indicates that a proxy is being generated by Spring. A proxy can be generated for various reasons but the most common one for controllers is security, the #PreAuthorize annotation comes to mind. Another option is the #Timed annotation to do monitoring on the controller.
As your initBinder method is private this will be invoked directly on the proxy and not on the actual bean instance. This is due to a class proxy being created, which works by extending your class. A method is being generated with the same signature and the interceptor is invoked and then the call is passed on to the actual method. However this will only work for public and protected methods not for private or final methods. Those will be directly invoked on the empty proxy (the fields are null as that doesn't need the dependencies).
As your method is private it is invoked on the proxy. The proxy doesn't have the fields set, so the userValidator is null. So effectivly there is no Validator being set on the WebDataBinder and thus no validation will and can be done.
To fix, make the method public (or protected) so that it will properly be called on the actual bean and not the proxy.
For the future and as a rule of thumb you probably want your methods with annotations in a controller to be public anyway instead of `private.
The issue was in private access modifier for initBinder method combined with #PreAuthorize annotation on some UserController methods.
Many thanks to #M.Deinum for patience and help.
Basically, Spring crashes with this error "value [null]; codes [NotNull.user.email,NotNull.email,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email]]; default message [must not be null], Field error in object 'user' on field 'firstName': rejected value [null]; codes" everytime I try to submit the completeProfile form. So, naturally, I looked at what data was saved after clicking submit on form. It turns out that the User object saved was having all the submitted property from the form as null. I checked inside the controller what was passed inside, all submitted properties from the form were null. Why does this happen?
User.java
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(min = 2, max = 80)
private String firstName;
#NotNull
#Size(min= 2, max = 80)
private String lastName;
#NotNull
#Email
private String email;
private Boolean enabled = false;
private String password = "";
private String role = "AUTHOR";
private String location = "";
private String topics = "";
private String job = "";
public Long getId() {
return id;
}
public void setId(Long 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;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getTopics() {
return topics;
}
public void setTopics(String topics) {
this.topics = topics;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
completeProfile.html
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>Complete your profile</p>
<form action="#" th:action="#{/completed-profile}" th:object="${user}" method="post">
<p>Email: <p th:text="${user.email}"></p>
<table>
<tr>
<td>Location:</td>
<td><input type="text" th:field="*{location}" /></td>
<td th:if="${#fields.hasErrors('location')}" th:errors="*{location}"></td>
</tr>
<tr>
<td>Job</td>
<td><input type="text" th:field="*{job}" /></td>
<td th:if="${#fields.hasErrors('job')}" th:errors="*{job}"></td>
</tr>
<tr>
<td>Topics of interest</td>
<td><input type="text" th:field="*{topics}" /></td>
<td th:if="${#fields.hasErrors('topics')}" th:errors="*{topics}"></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" th:field="*{password}" /></td>
<td th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></td>
<td th:text="${password_error}"></td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
ProfileController.java
#Controller
public class ProfileController {
UserRepository userRepository;
public ProfileController(UserRepository userRepository) {
this.userRepository = userRepository;
}
#RequestMapping(value = {"/profile", "/profile.html"}, method = RequestMethod.GET)
public String profileForm(){
return "auth/profile";
}
#RequestMapping(value = "/complete-profile", method = RequestMethod.GET)
public String completeProfileForm(){
return "auth/completeProfile";
}
#RequestMapping(value = "/completed-profile", method = RequestMethod.POST)
public String submitProfileForm(#Valid User user, BindingResult bindingResult, Model model){
System.err.println(user.getEmail());
if (user.getPassword() == null || user.getPassword().equals("")){
// model.addAttribute("password_error", "password cannot be null");
model.addAttribute("user", user);
return "auth/completeProfile";
}
if(bindingResult.hasErrors()){
System.out.println(bindingResult.getAllErrors());
return "auth/completeProfile";
}
userRepository.save(user);
return "auth/login";
}
}
As the title says, I'm having difficulty in my Java + Spring/Hibernate project where when I go to update and Employee or Customers information, it removes their role from the employee_roles/customer_roles table in the database.
I had this same issue with the username being removed however, I was able to work around this by creating a hidden input form for the username in the html page.
The files for running this project can be found below, if you find that you can't get it to work properly on Mac, that may be because you will need to change the jdbc.url to just: jdbc:mysql://localhost:3306/spring_pie_deal
https://drive.google.com/file/d/1YAdaGCQXH-tjDy8EfnV0WagcV33I8ADM/view?usp=sharing
Employee:
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "phone_number")
private String phoneNumber;
#Column(name = "address")
private String address;
#Column(name = "zipcode")
private String zipcode;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "employee_roles",
joinColumns = #JoinColumn(name = "employee_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Collection<Role> roles;
public Employee() {
}
public Employee(String userName, String password, String firstName, String lastName, String email, String phoneNumber, String address, String zipcode) {
this.userName = userName;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.phoneNumber = phoneNumber;
this.address = address;
this.zipcode = zipcode;
}
public Employee(String userName, String password, String firstName, String lastName, String email, String phoneNumber, String address, String zipcode, Collection<Role> roles) {
this.userName = userName;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.phoneNumber = phoneNumber;
this.address = address;
this.zipcode = zipcode;
this.roles = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
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;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public Collection<Role> getRoles() {
return roles;
}
public void setRoles(Collection<Role> roles) {
this.roles = roles;
}
#Override
public String toString() {
return "Employee{" +
"id=" + id +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
", address='" + address + '\'' +
", zipcode='" + zipcode + '\'' +
", roles=" + roles +
'}';
}
}
Role:
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
public Role() {
}
public Role(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Role{" + "id=" + id + ", name='" + name + '\'' + '}';
}
}
EmployeeDAOImpl:
#Repository
public class EmployeeDaoImpl implements EmployeeDAO{
#Autowired
private SessionFactory sessionFactory;
// define getEmployee method
#Override
public Employee getEmployee(Long theId) {
// get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// get Employee by the id
Employee theEmployee = currentSession.get(Employee.class, theId);
// return the employee
return theEmployee;
}
// define saveEmployee method
#Override
public void saveEmployee(Employee theEmployee) {
// get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// save Employee by the id
currentSession.saveOrUpdate(theEmployee);
}
// define deleteEmployee method
#Override
public void deleteEmployee(Long theId) {
// get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// delete Employee by id
currentSession.delete(theId);
}
#Override
public List<Employee> getEmployees() {
// get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// create query
Query<Employee> theQuery =
currentSession.createQuery("from Employee order by lastName", Employee.class);
// apply result list to variable list
List<Employee> theEmployees = theQuery.getResultList();
// return the result list variable
return theEmployees;
}
#Override
public Employee findByUserName(String userName) {
// get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// now create query... where username(from database) matches uName(direct relationship with userName1)
Query<Employee> theQuery = currentSession.createQuery("from Employee where username=:uName", Employee.class);
theQuery.setParameter("uName", userName);
Employee theEmployee = null;
try {
theEmployee = theQuery.getSingleResult();
}
catch (Exception e) {
theEmployee =null;
}
return theEmployee;
}
#Override
public void save(Employee employee) {
// get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// create or save user
currentSession.saveOrUpdate(employee);
}
}
Employee-form.jsp:
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%# taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html>
<head>
<title>Update Employee</title>
<link type="text/css"
rel="stylesheet"
href="${pageContext.request.contextPath}/resources/css/style.css">
<link type="text/css"
rel="stylesheet"
href="${pageContext.request.contextPath}/resources/css/add-customer-style.css">
</head>
<body>
<div id="wrapper">
<div id="header">
<h2>ERM - Employee Relationship Manager</h2>
</div>
</div>
<div id="container">
<h3>Update Employee</h3>
<form:form action="saveEmployee"
modelAttribute="employee">
<!-- need to associate this data with a employee id -->
<form:hidden path="id" />
<table>
<tbody>
<tr>
<td><form:input type="hidden" path="userName" /></td>
</tr>
<tr>
<td><form:input type="hidden" path="password" /></td>
</tr>
<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>Phone Number:</label></td>
<td><form:input path="phoneNumber" /></td>
</tr>
<tr>
<td><label>Address:</label></td>
<td><form:input path="address" /></td>
</tr>
<tr>
<td><label>Zip Code:</label></td>
<td><form:input path="zipcode" /></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save" class="save" /></td>
</tr>
</tbody>
</table>
</form:form>
<div style="clear; both;"></div>
<!-- Add a logout button -->
<form:form action="${pageContext.request.contextPath}/logout"
method="POST">
<input type="submit" value="Logout" />
</form:form>
<p>
Back to List
</p>
</div>
</body>
</html>
I'm creating a web-application with spring MVC, hibernate and thymeleaf.
I have a page where I can manage users, on this page you should be able to place and remove users from groups.
I am doing this with 2 multiple select boxes.
I added a jquery script what handles the movement of users from the one select box to the other one.
But when i submit, my Group.users object list is empty and I do not get any exceptions.
Does anyone has some advice?
Thanks in advance.
Edit
I just discovered that all thymeleaf attributes inside the html tag "option", aren't compiled. Except for the th:each attr.
So it's pretty clear the problem is in my thymeleaf file.
Thymeleaf / edit.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<div th:replace="template :: css"></div>
<title>Edit group</title>
</head>
<body>
<script>
$(document).ready(function() {
$(".clickable").click(function() {
if ($(this).hasClass("selected")) {
$(this).removeClass("selected").addClass("unselected");
$('#userGroupContainer').append(this);
$("option:selected").css("background-color", "red");
} else {
$(this).removeClass("unselected").addClass("selected");
$('#userGroupContainerSelected').append(this);
$("option:selected").css("background-color", "green");
}
});
});
</script>
<div id="bodyWrap">
<div th:replace="template :: logo">Logo</div>
<div th:replace="template :: nav">Nav</div>
<div th:replace="template :: messages">Header</div>
<div id="backGround">
<div id="contentWrap">
<form action="#{edit}"
th:action="#{${#httpServletRequest.servletPath}}"
th:object="${group}" th:method="post">
<h1 th:unless="${group.id}">Add group</h1>
<h1 th:if="${group.id}">Edit group</h1>
<hr />
<div th:replace="template :: messages">Header</div>
<div class="newFile">
<input type="hidden" th:field="*{id}" />
<table class="newFile">
<tr>
<th>Name:</th>
<td><input type="text" size="50" th:field="${group.name}" /></td>
</tr>
<tr>
<th>Description:</th>
<td><textarea th:field="${group.description}"></textarea></td>
</tr>
<tr>
<td> </td>
</tr>
</table>
<br /> users <br />
<select multiple="multiple" id="userGroupContainer">
<option th:each="u : ${userNotInGroup}" th:text="${u.displayName}" class="clickable unselected" th:value="${u}" ></option>
</select>
<!-- It's all about the select box under this comment -->
<select multiple="multiple" id="userGroupContainerSelected" th:field="*{users}">
<option th:each="ug, rowStat : ${group.users}" th:text="${ug.displayName}" th:value="${ug}" class="clickable selected">Selected</option>
</select>
<div class="form-actions">
<button th:unless="${group.id}" type="submit">Add</button>
<button th:if="${group.id}" type="submit">Update</button>
</div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
example of the 2 multiple select boxes:
$(document).ready(function() {
$(".clickable").click(function() {
if ($(this).hasClass("selected")) {
$(this).removeClass("selected").addClass("unselected");
$('#userGroupContainer').append(this);
$("option:selected").css("background-color", "red");
} else {
$(this).removeClass("unselected").addClass("selected");
$('#userGroupContainerSelected').append(this);
$("option:selected").css("background-color", "green");
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
<select multiple="multiple" id="userGroupContainer">
<option class="clickable unselected" >Example</option>
</select>
<select multiple="multiple" id="userGroupContainerSelected" th:field="*{users}">
<option class="clickable selected">Selected</option>
</select>
Controller:
#RequestMapping(value = "/management/edit/{groupId}", method = RequestMethod.GET)
public String editGroup(ModelMap model, Principal principal, #PathVariable("groupId") Long groupId) {
Group group = groupService.findGroupById(groupId);
User user = new User();
List<User> userNotInGroup = userService.findUsersNotInGroup(group);
model.addAttribute("userNotInGroup", userNotInGroup);
model.addAttribute("group", group);
return "management/groups/edit";
}
#RequestMapping(value = "/management/edit/{groupId}", method = RequestMethod.POST)
public String editGroup(#Valid Group group, BindingResult result, Model model, #PathVariable("groupId") Long groupId) {
model.addAttribute("group", group);
System.out.println("USERS: " + group.getUsers());
groupService.saveGroup(group);
return "redirect:/management/list";
}
Group Entity / object:
#Entity
#Table(name = "GROUPS")
public class Group extends DomainObject {
private static final long serialVersionUID = ;
#Id
#GeneratedValue(generator = "GROUPS_SEQ", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "GROUPS_SEQ", sequenceName = "GROUPS_SEQ")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#JoinTable(name = "USERS_GROUPS")
#ManyToMany(fetch = FetchType.EAGER)
private Collection<User> users;
#JoinTable(name = "GROUPS_ROLES")
#ManyToMany
private Collection<Role> roles;
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Collection<Role> getRoles() {
return roles;
}
public void setRoles(Collection<Role> roles) {
this.roles = roles;
}
}
User Entity / Object:
#Entity
#Table(name = "USERS")
public class User extends DomainObject implements UserDetails {
private static final long serialVersionUID = ;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#NotNull
private Long ID;
#Column(name = "DISPLAY_NAME")
#NotNull
private String displayName;
#Column(name = "EMAIL_ADDRESS")
#NotNull
private String emailAddress;
#Column(name = "PASSWORD")
private String password;
#Column(name = "USERNAME")
#NotNull
private String username;
#Column(name = "LAST_LOGIN")
private Date lastLogin;
#Column(name = "MODIFIED_DATE")
private Date modifiedDate;
#Column(name = "MODIFIED_BY")
private String modifiedBy;
#Transient
private Collection<? extends GrantedAuthority> authorities;
private boolean admin;
#Nullable
#JoinTable(name = "USERS_GROUPS")
#ManyToMany
private Collection<Group> groups;
public Date getLastLogin() {
return lastLogin;
}
public void setLastLogin(Date lastLogin) {
this.lastLogin = lastLogin;
}
public Collection<Group> getGroups() {
return groups;
}
public void setGroups(Collection<Group> groups) {
this.groups = groups;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
public void setPassword(String password) {
this.password = password;
}
public void setUsername(String username) {
this.username = username;
}
public boolean isAdmin() {
return admin;
}
public void setAdmin(boolean admin) {
this.admin = admin;
}
#Override
public Long getId() {
return ID;
}
#Override
public void setId(Long id) {
this.ID = id;
}
}
I have a problem when i validate the form. the form takes into account validation, but the error messages are not displayed.
Controller :
#RequestMapping(value = "/saveUser", method=RequestMethod.POST)
public String addUtilisateur(#Valid user util, BindingResult bindingResult, Model model)throws Exception{
if(bindingResult.hasErrors()){
model.addAttribute("user", new user());
return "login";
}
metier.ajoutUser(util);
return "login";
}
User.java :
#Entity
public class user implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long idUser;
#Column(unique=true)
#NotEmpty
private String login;
#NotEmpty
private String password;
private String action;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Long getIdUser() {
return idUser;
}
public void setIdUser(Long idUser) {
this.idUser = idUser;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public user() {
super();
// TODO Auto-generated constructor stub
}
public user(String login, String password) {
super();
this.login = login;
this.password = password;
}
login.jsp :
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%#taglib uri="http://www.springframework.org/tags/form" prefix="f"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:form modelAttribute="user" action="saveUser" method="post">
<table>
<tr>
<td>Login</td>
<td><f:input path="login"/>
<f:errors path="login" cssClass="errors"></f:errors> </td>
</tr>
<tr>
<td>Pass word</td>
<td><f:input type="password" path="password"/>
<f:errors path="password" cssClass="errors"></f:errors> </td>
</tr>
<tr>
<td><input type="submit" value="Login"></td>
</tr>
</table>
</f:form>
</body>
</html>
there's there a solution for that error messages are displayed well ??