Thymeleaf access object in outerloop from innerloop - spring-boot

i am looping through a list of "roles" , inside each one i loop through a list of "notifications" ( a role can have many notifications )
<div class="row justify-content-center">
<div class="col-sm-3" th:each="role : ${roles}">
<div class="card">
<div class="card-body">
<h5 class="card-title" th:text="${role.name}"></h5>
Notifications :
<div class="input-group mb-3">
<th:block th:each="notif : ${notif_list}">
<div class="custom-control custom-control-inline">
<input type="checkbox" class="custom-control-input"
th:id="${notif.id_notif_type}"
th:value="${notif.id_notif_type}" th:field="${role.notifs}">
<label class="custom-control-label" th:text="${notif.type}"
th:for="${notif.id_notif_type}"></label>
</div>
</th:block>
</div>
</div>
</div>
</div>
</div>
i want access an object "role" inside the notification loop by using th:field="${role.notifs}
here is my Role class :
#Entity
#Table(name = "roles")
#Transactional
public class Role implements Serializable{
public Role() {
}
public Role(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
#Id
#Column(name = "role_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinTable(name = "roles_notifs", joinColumns = #JoinColumn(name = "role_id"), inverseJoinColumns = #JoinColumn(name = "notif_id"))
private Set<NotifType> notifs = new HashSet<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<NotifType> getNotifs() {
return notifs;
}
public void setNotifs(Set<NotifType> notifs) {
this.notifs = notifs;
}
public void addNotif(NotifType notif) {
notifs.add(notif);
}
public void removeNotif(NotifType notif) {
notifs.remove(notif);
}
#Override
public String toString() {
return name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Role other = (Role) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
i got an error on ${role.notifs} , so how can i access the outer object "role" from the inner loop.
i got this error Neither BindingResult nor plain target object for bean name 'role' available as request attribute

Related

Thymeleaf #sets.contains() always returning false despite usage according to documentation

Preface
I have a spring boot application with a User entity with a set of Role.
On the edit user template, I am displaying the user's roles with a <select>multiple. When rending the view of a existing User with its set of Role, I am trying to only mark as selected the roles within the set.
Thymeleaf provides two tools for this:
th:selected: Which expects a boolean value (true being selected)
#sets: Which provides a handful of useful methods similar to java.util.Set, the one being used in this case is contains().
The problem
When adding to the model a found User and all the possibles Role in the form of a HashSet, using #sets.contains() always return false when using the found user's roles and all the roles as parameters, therefore not selecting the user's roles when loading the form.
If I use the notation th:selected="${{user.roles}}" notation all the options are selected (even those the user does not posses).
The Code
User
public class User
{
private Long id;
private String username;
private String password;
private String passwordConfirm;
private Set<Role> roles;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
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;
}
#Transient
public String getPasswordConfirm()
{
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm)
{
this.passwordConfirm = passwordConfirm;
}
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "users_role", joinColumns = #JoinColumn(name = "users_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
public Set<Role> getRoles()
{
return roles;
}
public void setRoles(Set<Role> roles)
{
this.roles = roles;
}
}
Role
public class Role
{
private Long id;
private String name;
private Set<User> users;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
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;
}
#ManyToMany(mappedBy = "roles")
public Set<User> getUsers()
{
return users;
}
public void setUsers(Set<User> users)
{
this.users = users;
}
}
Controller
#Controller
#RequestMapping("/admin")
public class AdminController
{
#Autowired
UserService userService;
#Autowired
RoleService roleService;
#RequestMapping("/user/edit/{id}")
public String editUser(Model model, #PathVariable("id") long id)
{
User user = userService.findByUserId(id);
HashSet<Role> foundRoles = roleService.getAllRoles();
model.addAttribute("user", user);
model.addAttribute("userRoles", foundRoles);
return "admin/adminUserDetail";
}
}
The form
<form role="form" th:action="#{/registration}" method="POST"
th:object="${user}">
<div th:if="${#fields.hasErrors('*')}">
<div class="alert alert-danger" role="alert">
<h3 class="alert-heading">It seems we have a couple problems with your input</h3>
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</div>
</div>
<div class="form-group">
<label>Username: </label> <input class="form-control" type="text" th:field="${user.username}"
placeholder="Username" name="username"/>
<label>Password: </label> <input class="form-control" type="password" th:field="${user.password}"
placeholder="Password" name="password"/>
<label>Password Confirm: </label> <input type="password"
th:field="${user.passwordConfirm}" class="form-control"
placeholder="Password Confirm"/>
<select class="form-control" multiple="multiple">
<option th:each="role : ${userRoles}"
th:value="${role.id}"
th:selected="${#sets.contains(user.roles, role)}"
th:text="${role.name}">Role name
</option>
</select>
<button type="submit" class="btn btn-success">Update</button>
</div>
</form>
When using a Set the object in there must implement both hashCode and equals as that is used to determine if an object is already in the Set. Unless it is a SortedSet which uses either a Comparator or the natural order expressed through your object implementing Comparable.
As you don't do either of those using contains will simply always return false even for a seemingly same Role instance. Because according to the contract they aren't.
To fix implement the equals and hashCode method in your User and Role object.
public class Role {
public int hashCode() {
return Objects.hash(this.name);
}
public boolean equals(Object o) {
if (o == this) { return true; }
if (o == null || !(o instanceof Role) ) { return false; }
return Objects.equals(this.name, ((Role) o).name);
}
}
Something along those lines should do the trick.

Get list within a list using Thymeleaf

I have two tables one names "State" another named "City"
My city model
#Entity
#Table(name = "city")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String city;
#ManyToOne
private State state;
public City() {
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCity() {
return this.city;
}
public void setCity(String city) {
this.city = city;
}
public State getState() {
return this.state;
}
public void setState(State state) {
this.state = state;
}
}
my state model
#Entity
#Table(name = "state")
public class State implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String state;
#OneToMany(mappedBy = "state")
private List<City> cities;
public State() {
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getState() {
return this.state;
}
public void setState(String state) {
this.state = state;
}
public List<City> getCities() {
return this.cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
public City addCity(City city) {
getCities().add(city);
city.setState(this);
return city;
}
public City removeCity(City city) {
getCities().remove(city);
city.setState(null);
return city;
}
}
my CityRepository
public interface CityRepository extends CrudRepository<City, Long> {
List<City> findByState(String city);
}
my StateRepository
public interface StateRepository extends CrudRepository<State, Long> {
List<State> findByState(String state);
}
my controller
#Controller
public class IndexController {
#Autowired
StateRepository stateRepository;
#Autowired
CityRepository cityRepository;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Model model) {
model.addAttribute("title", "");
Iterable<State> stateIterable = stateRepository.findAll();
for (State state : stateIterable) {
System.out.println(state.getState());
}
model.addAttribute("stateIterable", stateIterable);
return "index";
}
}
and the relevant code for Theymeleaf
<div class="col-md-4">
<div class="panel-group" id="panel-790692">
<th:block th:each="state : ${stateIterable}">
<div class="panel panel-default">
<div class="panel-heading">
<a class="panel-title" data-toggle="collapse"
data-parent="#panel-790692"
th:href="|#panel-element-${#strings.replace(state,' ','-')}|"
th:text="${state.state}">State Name</a>
</div>
<th:block th:each="city : ${state.getCities}">
<div th:id="|panel-element-${#strings.replace(state,' ','-')}|"
class="panel-collapse collapse in">
<div class="panel-body" th:text="city.city"></div>
</div>
</th:block>
</div>
</th:block>
</div>
</div>
Right now I am getting this error
"Property or field 'getCities' cannot be found on object"
Is it possible to get the cities and loop through the cities using Themeleaf? If so how can it be done?
Try with replacing ${state.getCities} with ${state.cities}
<th:block th:each="city : ${state.cities}">
<div th:id="|panel-element-${#strings.replace(state,' ','-')}|"
class="panel-collapse collapse in">
<div class="panel-body" th:text="city.city"></div>
</div>
</th:block>

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.

Spring form:select multiple selected value?

I have this edit form:
I want that the roles of the user get selected. If this were one-to-many relation I know that I can do something like this:
<form:label path="roles">Roles:</form:label>
<form:select multiple="true" path="roles">
<c:forEach items="${roles}" var="rol">
<c:choose>
<c:when
test="${usuarioEdit.rol.id ==rol.id}">
<option value="${rol.id}" selected="selected">${rol.nombre}</option>
</c:when>
<c:otherwise>
<option value="${rol.id}">${rol.nombre}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</form:select>
<form:errors cssStyle="color:red" path="roles"></form:errors>
But this is a many-to-many relation. How can I get selected the options in an edit form?. Is there an easy form?
This code works, but I wonder if spring gives any facilities:
<form:select multiple="true" path="roles">
<c:forEach items="${roles}" var="rol">
<c:set var="isSelected" value="false" />
<c:forEach items="${rolesUsu}" var="rolUsu">
<c:if test="${rolUsu.getRol().getId()==rol.id}">
<c:set var="isSelected" value="true" />
</c:if>
</c:forEach>
<c:choose>
<c:when test="${isSelected}">
<option value="${rol.id}" selected="selected">${rol.nombre}</option>
</c:when>
<c:otherwise>
<option value="${rol.id}">${rol.nombre}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</form:select>
Edit:
In my controller I have:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "roles",
new RolCollectionEditor(Set.class, rolDao));
}
RolCollectionEditor:
public class RolCollectionEditor extends CustomCollectionEditor {
private final RolDAO rolDao;
public RolCollectionEditor(Class<?> collectionType, RolDAO rolDao) {
super(collectionType);
this.rolDao = rolDao;
}
#Override
protected Object convertElement(Object element) {
String rolId = (String) element;
Rol rol = rolDao.findById(rolId);
Usuario_Rol usuRol = new Usuario_Rol();
//Agregamos un usuario vacio temporal
//y lo sobreescribimos en el controlador
Usuario usuario = new Usuario();
usuRol.setUsuario(usuario);
usuRol.setRol(rol);
usuRol.setFechaCreacion(new Date());
usuRol.setFechaModificacion(new Date());
usuRol.setStatus("activo");
return usuRol;
}
}
Here Usuario_Rol is an intermediate table for the many to many relation, that have other attributes besides the userId and rolId.
Edit2:
Rol class:
#Entity
#Table(name = "rol", uniqueConstraints = { #UniqueConstraint(columnNames = "nombre") })
public class Rol implements Serializable{
#Id
#Column(name = "_id")
private String id;
#Column(name = "nombre")
#NotNull
private String nombre;
#Column(name = "descripcion")
private String descripcion;
#Column(name = "status")
private String status;
#Column(name = "fechaCreacion")
private Date fechaCreacion;
#Column(name = "fechaModificacion")
private Date fechaModificacion;
#Column(name = "fechaSincronizacion")
private Date fechaSincronizacion;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "usuarioRol_pk.rol", orphanRemoval = true, cascade=CascadeType.ALL)
private Set<Usuario_Rol> usuarios = new HashSet<Usuario_Rol>(0);
//getters and setters
#Override
final public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((nombre == null) ? 0 : nombre.hashCode());
return result;
}
#Override
final public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Rol))
return false;
Rol other = (Rol) obj;
if (nombre == null) {
if (other.nombre != null)
return false;
} else if (!nombre.equals(other.nombre))
return false;
return true;
}
Class Usuario:
#Entity
#Table(name = "usuario", uniqueConstraints = {
#UniqueConstraint(columnNames = "login"),
#UniqueConstraint(columnNames = "correo") })
public class Usuario implements Serializable {
#Id
#Column(name = "_id")
private String id;
#Column(name = "nombre")
#NotEmpty
private String nombre;
#Column(name = "apellido")
#NotEmpty
private String apellido;
#Column(name = "login")
#Size(min = 4)
#NotEmpty
private String login;
#Column(name = "password")
#NotEmpty
#Size(min = 4)
private String password;
#Column(name = "salt")
private String salt;
#Column(name = "correo")
#NotEmpty
#Email
private String correo;
#Column(name = "token")
private String token;
#Column(name = "status")
private String status;
#Column(name = "fechaUltimoLogin")
private Date fechaUltimoLogin;
#Column(name = "fechaCreacion")
private Date fechaCreacion;
#Column(name = "fechaModificacion")
private Date fechaModificacion;
#Column(name = "fechaSincronizacion")
private Date fechaSincronizacion;
#NotEmpty
#OneToMany(fetch = FetchType.EAGER, mappedBy = "usuarioRol_pk.usuario", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<Usuario_Rol> roles = new HashSet<Usuario_Rol>(0);
//constructor, getters and setters.
#Override
final public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((login == null) ? 0 : login.hashCode());
return result;
}
#Override
final public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Usuario))
return false;
Usuario other = (Usuario) obj;
if (login == null) {
if (other.login != null)
return false;
} else if (!login.equals(other.login))
return false;
return true;
}
Intermediate class:
#Entity
#Table(name = "usuario_rol")
#AssociationOverrides({
#AssociationOverride(name = "usuarioRol_pk.usuario", joinColumns = #JoinColumn(name = "idUsuario")),
#AssociationOverride(name = "usuarioRol_pk.rol", joinColumns = #JoinColumn(name = "idRol"))
})
public class Usuario_Rol implements Serializable{
#EmbeddedId
private Usuario_RolId usuarioRol_pk = new Usuario_RolId();
#Temporal(TemporalType.DATE)
#Column(name = "fechaCreacion")
private Date fechaCreacion;
#Temporal(TemporalType.DATE)
#Column(name = "fechaModificacion")
private Date fechaModificacion;
#Temporal(TemporalType.DATE)
#Column(name = "fechaSincronizacion")
private Date fechaSincronizacion;
#Column(name = "status")
private String status;
//gettters, setters
#Override
final public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((usuarioRol_pk == null) ? 0 : usuarioRol_pk.hashCode());
return result;
}
#Override
final public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Usuario_Rol))
return false;
Usuario_Rol other = (Usuario_Rol) obj;
if (usuarioRol_pk == null) {
if (other.usuarioRol_pk != null)
return false;
} else if (!usuarioRol_pk.equals(other.usuarioRol_pk))
return false;
return true;
}
Usuario_RolId:
#Embeddable
public class Usuario_RolId implements Serializable{
#ManyToOne
private Usuario usuario;
#ManyToOne
private Rol rol;
public Usuario getUsuario() {
return usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
public Rol getRol() {
return rol;
}
public void setRol(Rol rol) {
this.rol = rol;
}
#Override
final public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((rol == null) ? 0 : rol.hashCode());
result = prime * result + ((usuario == null) ? 0 : usuario.hashCode());
return result;
}
#Override
final public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Usuario_RolId))
return false;
Usuario_RolId other = (Usuario_RolId) obj;
if (rol == null) {
if (other.rol != null)
return false;
} else if (!rol.equals(other.rol))
return false;
if (usuario == null) {
if (other.usuario != null)
return false;
} else if (!usuario.equals(other.usuario))
return false;
return true;
}
This last class is used for the trick of simulating a many to many relation. I have followed this tutorial: http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/
Why are you writing your own? Spring should be able to do that for you. Instead of a <c:forEach /> replace that whole block with a <form options .. /> tags. Spring will then be able to do the selection itself (you might need a Converter or PropertyEditor for that).
<form:select multiple="true" path="roles" items="${roles}" itemLabel="nombre" itemValue="id" />
Something along these lines...
Links:
Form Options documentation
Form Select documentation
Reference Guide
If you use this:
<form:select multiple="true" path="roles" items="${roles}" itemLabel="nombre" itemValue="id" />
you need to override toString() method of Usuario_Rol, in the right way for your class, to ensure that Spring pre-selects the initial values for you.
If I understand the question correctly, what you want is your Spring tag to generate HTML like this one:
<select id="roles" name="roles multiple="multiple">
<option value="1">Administrador</option>
<option value="2">Usuario avanzado</option>
<option value="3" selected="selected">Usuario </option>
<option value="4" selected="selected">Invitado</option>
</select>
As you can see, two values are selected ("Usuario" and "Invitado").
The "roles" model attribute that "path" refers to in your Spring tag has be an array instead of a single value. It's as easy as that. Please be aware that I set the array by hand in my controller. I am not familiar with the implications on the ORM side of your code.
I think you want to expect output like this:
<select id="roles" name="roles" multiple="multiple">
<option value="1">Administrador</option>
<option value="2" selected="selected">Usuario avanzado</option>
<option value="3" selected="selected">Usuario </option>
</select>
In model class you can create method for "roles" which should return array. Add your business complexity in that method.
public Integer[] getRoles(){
Integer[] selectedRoles = {2,3};
return selectedRoles;
}
In JSP:
<form:select multiple="true" path="roles">
<form:options items="${...}" itemValue="..." itemLabel="..."/>
</form>

Resources