Spring Boot, Thymeleaf, ManyToMany checkboxes evaluation - spring-boot

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.

Related

EL1007E: Property or field 'name' cannot be found on null

I am using a Spring framework with thymeleaf and mysql.
I am getting the error
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null
This error is being caused by the ${selectProject.client.name} in the html code.
I have written a form to allow a user to add information about a project. As part of the form, there is a dropdown list of the different project names. However, in the dropdown list I also want it to give the name of the client alongside the name of the project.
My html code:
<form action="#" id="informationForm" method="post" th:action="#{/add-information}" th:object="${information}">
<div class="row">
<div class="form-group col-12">
<label class="col-form-label" for="project">Project</label>
<select class="form-control" id="project" th:field="*{project}">
<option value="0">Please select a project</option>
<option th:each="selectProject : ${projectList}"
th:text="${selectProject.client.name} + ' - ' +${selectProject.name}"
th:value="${selectProject.id}"></option>
</select>
Here is the information controller:
#Controller
#Slf4j
#SessionAttributes({"project", "information"})
public class InformationController {
private final InformationService informationService;
private final ProjectsService projectsService;
#Autowired
public InformationController(final ProjectsService projectsService,
final InformationService informationService) {
this.projectsService = projectsService;
this.informationService = informationService;
}
#ModelAttribute("information")
public Information getInformation() {
return informationService.createEmptyInformation();
}
#ModelAttribute("projectList")
public List<Project> getProjects() {
return projectsService.getProjects();
}
#GetMapping("add-information")
public String showAddInformationForm(Model model, #ModelAttribute("information") Information information) {
information = informationService.createEmptyInformation();
model.addAttribute(information);
return "add-information";
}
#PostMapping("add-information")
public String addInformationForm(#Valid Information information, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-information";
}
Long id = informationService.createInformationFromInput(information);
return "redirect:/homepage";
}
The relevant methods in the information service are:
public Information createEmptyInformation() {
return new Information();
}
public Long createInformationFromInput(Information information) {
try {
informationDao.save(information);
} catch (Exception ex) {
log.error("Failed to save information for {} because of {}", information.getId(), ex.getLocalizedMessage());
}
return information.getId();
}
and in the projects services:
public List<Project> getProjects() {
return projectDao.findAll();
}
Clients have a many to one relationship with projects
Project domain:
#Getter
#Setter
#Entity
#Table(name = "projects")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "client_id")
private Client client;
#Column(name = "name")
private String name;
Client domain:
#Getter
#Setter
#Entity
#Table(name = "clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Name is required")
#Size(min = 5, max = 80, message = "Name must be between 5 and 80 characters")
#Column(name = "name", nullable = false)
private String name;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "client_id")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private List<Project> projects;
Information domain:
#Getter
#Setter
#Entity
#Table(name = "information")
public class Information {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "project_id")
private Project project;
#Column(name = "date")
private Date date;

Changes not persisted in database in a Spring Boot application using Spring JPA

I have a Spring Boot application that needs adds a post to a feed list. A post is written by a user and consists of a content and several attachments. The database has 3 tables: post, attachment and user.
The main class of the application is:
#SpringBootApplication
public class SocialMediaApplication {
public static void main(String[] args) {
SpringApplication.run(SocialMediaApplication.class, args);
}
}
The entities are the following:
Post.java
#Entity
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String content;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#Column(nullable = false)
private Timestamp createdAt;
#Column
private String location;
#OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Attachment> attachmentList;
#OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Rating> ratingList;
public Post() {
}
public Post(String content, User user, Timestamp createdAt, String location, List<Attachment> attachmentList, List<Rating> ratingList) {
super();
this.content = content;
this.user = user;
this.createdAt = createdAt;
this.location = location;
this.attachmentList = attachmentList;
this.ratingList = ratingList;
}
// ...
}
Attachment.java
#Entity
public class Attachment implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Lob
#Column(length = 100_000, nullable = false)
private byte[] content;
#ManyToOne
#JoinColumn(name = "post_id")
private Post post;
public Attachment() {
}
public Attachment(byte[] content, Post post) {
super();
this.content = content;
this.post = post;
}
// ...
}
User.java
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
#Column(nullable = false)
private Date dateOfBirth;
#Column(nullable = false)
private String credential;
#Column(nullable = false)
private String password;
#Column
private String location;
#Lob
#Column(length = 100_000)
private byte[] photo;
#Column
private String motto;
public User() {
}
public User(String firstName, String lastName, Date dateOfBirth, String credential, String password,
String location, byte[] photo, String motto) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = dateOfBirth;
this.credential = credential;
this.password = password;
this.location = location;
this.photo = photo;
this.motto = motto;
}
// ...
}
All repositories extend CrudRepository and are annotated with #Transactional:
PostRepository.java
#Transactional
public interface PostRepository extends CrudRepository<Post, Long> {
}
AttachmentRepository.java
#Transactional
public interface AttachmentRepository extends CrudRepository<Attachment, Long> {
}
UserRepository.java
#Transactional
public interface UserRepository extends CrudRepository<User, Long> {
}
The controller that should add a post to the feed is the following:
#Controller
#RequestMapping("/post")
public class PostController {
#Autowired
PostRepository postRepository;
#GetMapping("/add")
public String greetingForm(Model model) {
model.addAttribute("post", new Post());
return "addPost";
}
#PostMapping("/add")
public String addPost(#ModelAttribute Post post, #RequestParam("attachment") MultipartFile uploadingFile) throws IOException {
User user = new User();
user.setId(1L);
post.setUser(user);
post.setCreatedAt(Timestamp.valueOf(LocalDateTime.now()));
List<Attachment> attachmentList = new ArrayList<>();
Attachment attachment = new Attachment();
attachment.setContent(uploadingFile.getBytes());
attachment.setPost(post);
attachmentList.add(attachment);
post.setAttachmentList(attachmentList);
List<Rating> ratingList = new ArrayList<>();
post.setRatingList(ratingList);
postRepository.save(post);
return "allPosts";
}
}
The addPost.html page has the following content:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Post</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Add Post</h1>
<form action="#" th:action="#{/post/add}" th:object="${post}" method="post" enctype="multipart/form-data">
<table border="0">
<tr>
<td>Content</td>
<td><textarea id="content" th:field="*{content}" rows="5" cols="50"></textarea></td>
</tr>
<tr>
<td>Location</td>
<td><input type="text" id="location" th:field="*{location}"/></td>
</tr>
<tr>
<td>Attachment</td>
<td><input type="file" id="attachment" name="attachment"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</td>
</tr>
</table>
</form>
</body>
</html>
The application.properties file has the following content:
spring.datasource.url=jdbc:mysql://localhost:3306/****?useSSL=false
spring.datasource.username=root
spring.datasource.password=****
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.id.new_generator_mappings=false
spring.jpa.show-sql=true
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
However, when I press the submit button, nothing is persisted into the database, although the queries are being displayed in the console:
Hibernate: insert into post (content, created_at, location, user_id) values (?, ?, ?, ?)
Hibernate: insert into attachment (content, post_id) values (?, ?)
What could be the cause?

Basic CRUD operation with composite Id (spring + hibernate)

Im trying to make a basic create operation with hibernate and spring, but i keep getting the message that a id is empty when it is not.So im thinking that it might be because, the entity uses a composite id, fun fact at least for me is that i don't have any problem deleting the entities.
The method im using
#RequestMapping(value="addPatientFamilyRelative",method = RequestMethod.POST)
public #ResponseBody String addPatientFamilyRelative(#RequestParam(value="idPatient")int idPatient,
#RequestParam(value="idRelative")int idRelative,
#RequestParam(value="idRelationship")int idRelationship)
{
Patient_Relative patientRelative = new Patient_Relative();
patientRelative.setIdRelationship(relationshipService.getById(idRelationship));
patientRelative.setPatient(patientService.getById(idPatient));
patientRelative.setRelative(relativeService.getRelative(idRelative));
prService.create(patientRelative);
return "$('#tblPatientFamilyPatientRelatives').ajax.reload();$('#tblPatientRelativesList').ajax.reload()";
}
Patient_Relative class
#Entity
#Table(name="Patient_Relative")
public class Patient_Relative implements Serializable{
/**
*
*/
private static final long serialVersionUID = -2670460334767266076L;
#EmbeddedId
#JoinColumn(name = "idRelative", referencedColumnName = "idRelative", insertable = false, updatable = false)
#ManyToOne(optional = false)
#JsonIgnore
private Relative relative;
#JoinColumn(name = "idRelationship", referencedColumnName = "idRelationship")
#ManyToOne
private Relationship idRelationship;
#JoinColumn(name = "idPatient", referencedColumnName = "idPatient", insertable = false, updatable = false)
#ManyToOne(optional = false)
#JsonIgnore
private Patient patient;
public Relative getRelative() {
return relative;
}
public void setRelative(Relative relative) {
this.relative = relative;
}
public Relationship getIdRelationship() {
return idRelationship;
}
public void setIdRelationship(Relationship idRelationship) {
this.idRelationship = idRelationship;
}
public Patient getPatient() {
return patient;
}
public void setPatient(Patient patient) {
this.patient = patient;
}
}
PatientRelativeId
#Embeddable
public class PatientRelativeId implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 2719758608242901070L;
#Column(name = "idPatient")
private int patientId;
#Column(name = "idRelative")
private int relativeId;
public PatientRelativeId() {
}
public PatientRelativeId(int patientId, int relativeId) {
this.patientId = patientId;
this.relativeId = relativeId;
}
public int getPatientId() {
return patientId;
}
public void setPatientId(int patientId) {
this.patientId = patientId;
}
public int getRelativeId() {
return relativeId;
}
public void setRelativeId(int relativeId) {
this.relativeId = relativeId;
}
}
i hope this is enough to get some ideas, i would have liked to add a column just for the id but i think im not able to do that anymore.
Thanks in advance
I hope this helps someone.
First , my Patient_Relative class was short 1 variable , the one that would store the composite id, so i added the variable PatientRelativeId compositeId.
Second, at the controller method all i had to do was set the values of the composite id , the patient and the relative , and then call the service to create the object.
#RequestMapping(value="addPatientFamilyRelative",method = RequestMethod.POST)
public #ResponseBody String addPatientFamilyRelative(#RequestParam(value="idPatient")int idPatient,
#RequestParam(value="idRelative")int idRelative,
#RequestParam(value="idRelationship")int idRelationship)
{
Patient_Relative patientRelative = new Patient_Relative();
PatientRelativeId id = new PatientRelativeId(idPatient, idRelative);
patientRelative.setPatienRelativeId(id);
patientRelative.setIdRelationship(relationshipService.getById(idRelationship));
patientRelative.setPatient(patientService.getById(idPatient));
patientRelative.setRelative(relativeService.getRelative(idRelative));
prService.create(patientRelative);
return "addRelative";
}

Using a drop-down list in a model driven action class in Struts2

I'm populating an <s:select> from database. The action class is model-driven.
#Namespace("/admin_side")
#ResultPath("/WEB-INF/content")
#ParentPackage(value="struts-default")
public final class TestAction extends ActionSupport implements Serializable, ValidationAware, Preparable, ModelDriven<Transporter>
{
#Autowired
private final transient SharableService sharableService=null;
private static final long serialVersionUID = 1L;
private Transporter transporter; //Getter and setter
private Long transporterId; //Getter and setter.
private List<Transporter> transporters; //Getter only.
#Action(value = "Test",
results = {
#Result(name=ActionSupport.SUCCESS, location="Test.jsp"),
#Result(name = ActionSupport.INPUT, location = "Test.jsp")},
interceptorRefs={#InterceptorRef(value="defaultStack", params={"validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "load"})})
public String load() throws Exception
{
return ActionSupport.SUCCESS;
}
#Validations(
requiredFields={#RequiredFieldValidator(fieldName="transporterId", type= ValidatorType.FIELD, key = "transporter.required")})
#Action(value = "testInsert",
results = {
#Result(name=ActionSupport.SUCCESS, location="Test.jsp", params={"namespace", "/admin_side", "actionName", "Test"}),
#Result(name = ActionSupport.INPUT, location = "Test.jsp")},
interceptorRefs={#InterceptorRef(value="defaultStack", params={"validation.validateAnnotatedMethodOnly", "true"})})
public String insert() {
System.out.println("Selected item in the drop box : "+transporterId);
return ActionSupport.SUCCESS;
}
#Override
public void prepare() throws Exception {
transporters=sharableService.getTransporterList();
}
#Override
public Transporter getModel() {
return transporter;
}
}
and the following is <s:select> :
<s:select id="transporterId"
name="transporterId"
list="transporters"
value="transporterId"
listKey="transporterId"
listValue="transporterName"
headerKey="" headerValue="Select"
listTitle="transporterName"/>
This works perfectly.
I need this <s:select> in another action class which implements ModelDriven<ZoneTable>.
The table structure is simple, transporter->zone_table->country->state->city. There exists a one-to-many relationship between these tables.
How can we have a model driven action class implementing ModelDrven<ZoneTable> in which Transporter can be mapped to <s:select>, something like?
#Namespace("/admin_side")
#ResultPath("/WEB-INF/content")
#ParentPackage(value="struts-default")
public final class ZoneAction extends ActionSupport implements Serializable, ValidationAware, Preparable, ModelDriven<ZoneTable>
{
#Autowired
private final transient ZoneService zoneService=null;
#Autowired
private final transient SharableService sharableService=null;
private ZoneTable entity=new ZoneTable(); //Getter and setter.
private Long transporterId; //Getter and setter.
private List<Transporter> transporters; //Getter only.
#Override
public ZoneTable getModel() {
return entity;
}
#Override
public void prepare() throws Exception {
transporters=sharableService.getTransporterList();
}
}
Doing like this doesn't work. It doesn't set the value of transporterId upon submission, since the action class is implementing ModelDriven<ZoneTable> and not ModelDriven<Transporter> like the first case.
Is this possible using the model driven approach?
EDIT:
ZoneTable.java
public class ZoneTable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "zone_id", nullable = false)
private Long zoneId;
#Column(name = "zone_name", length = 45)
private String zoneName;
#JoinColumn(name = "transporter_id", referencedColumnName = "transporter_id")
#ManyToOne(fetch = FetchType.LAZY)
private Transporter transporterId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "zoneTable", fetch = FetchType.LAZY)
private Set<ZoneCharge> zoneChargeSet;
#OneToMany(mappedBy = "zoneId", fetch = FetchType.LAZY)
private Set<Country> countrySet;
//Getters and setters + constructors.
}
Zone.jsp
<s:form namespace="/admin_side" action="Zone" validate="true" id="dataForm" name="dataForm" cssClass="search_form general_form">
<s:label key="label.zone.name" for="zone"/>
<s:textfield id="zoneName" name="zoneName" cssClass="validate[required, maxSize[45], minSize[2]] text-input text"/>
<s:fielderror fieldName="zoneName"/>
<s:label key="label.transporter.name" for="transporterId"/>
<s:select id="transporterId" name="transporterId" list="transporters" value="transporterId" listKey="transporterId" listValue="transporterName" headerKey="" headerValue="Select" listTitle="transporterName"/>
<s:fielderror fieldName="transporterId"/>
<s:text name="label.submit"/>
<s:submit id="btnSubmit" name="btnSubmit" value="Submit" action="AddZone"/>
</s:form>
Since this post has already a lot of code, I'm not posting the action class ZoneAction.java here. In case, it is needed, it is available here.
You need a converter to convert transporterId to Transporter Object. It goes like this:
package com.converter;
public class TransporterConverter extends StrutsTypeConverter {
#Override
public Object convertFromString(Map map, String[] strings, Class type) {
String value = strings[0]; // The value of transporterId submitted from the jsp
if (value != null && value.length() > 0) {
try {
Long longVal = Long.valueOf(value);
//Integer intVal = Integer.valueOf(value);
if (type == Transporter.class) {
Transporter data = find_transporter_from_the_back_by_transporter_id_using_longVal;
return data;
}
} catch (Exception ex) {}
}
return null;
}
#Override
public String convertToString(Map map, Object o) {
if ((o instanceof Transporter)) {
Transporter data = (Transporter) o;
//return the id of the Transporter Object
}
return null;
}
}
The next thing to do is to map this class in a file called xwork-conversion.properties. This file must reside in your classpath i.e. in classes directory. Enter the following entries in xwork-conversion.properties
package_of_transporter_class.Transporter=com.converter.TransporterConverter
I have not tested it, but I think it should work.
If you need more information on how type converters work, follow this url.

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