I have a problem with binding collections using spring and thymeleaf. Every time I send form, my object collections are set to null (User.postions), my example below:
My Controller:
#RequestMapping(value = urlFragment + "/add", method = RequestMethod.GET)
public String addPosition(Model model) {
HashSet<Position> positions = new HashSet<Position>(positionRepository.findByEnabledTrueOrderByNameAsc());
User employee = new User();
for (Position position : positions) {
employee.addPosition(position);
}
model.addAttribute("employee", employee);
return "crud/employee/add";
}
#RequestMapping(value = urlFragment + "/add", method = RequestMethod.POST)
public String processNewEmployee(Model model, #Valid #ModelAttribute("employee") User employee, BindingResult result) {
String templatePath = "crud/employee/add";
if (!result.hasErrors()) {
userRepository.save(employee);
model.addAttribute("success", true);
}
return templatePath;
}
And my employee form:
<form action="#" th:action="#{/panel/employee/add}" th:object="${employee}" method="post">
<div class="row">
<div class="col-md-6">
<label th:text="#{first_name}">First name</label>
<input class="form-control" type="text" th:field="*{userProfile.firstName}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label th:text="#{last_name}">Last name</label>
<input class="form-control" type="text" th:field="*{userProfile.lastName}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label th:text="#{email}">Email</label>
<input class="form-control" type="text" th:field="*{email}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label th:text="#{position}">Position</label>
<select th:field="*{positions}" class="form-control">
<option th:each="position : *{positions}"
th:value="${position.id}"
th:text="${position.name}">Wireframe
</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-5">
<div class="checkbox">
<button type="submit" class="btn btn-success" th:text="#{add_employee}">
Add employee
</button>
</div>
</div>
</div>
</form>
User entity:
#Entity
#Table(name="`user`")
public class User extends BaseModel {
#Column(unique = true, nullable = false, length = 45)
private String email;
#Column(nullable = false, length = 60)
private String password;
#Column
private String name;
#Column
private boolean enabled;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "user_role",
joinColumns = {#JoinColumn(name = "user_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "role_id", nullable = false)}
)
private Collection<Role> roles = new HashSet<Role>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "user_position",
joinColumns = {#JoinColumn(name = "user_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "position_id", nullable = false)}
)
private Collection<Position> positions = new HashSet<Position>();
public User() {
}
public User(String email, String password, boolean enabled) {
this.email = email;
this.password = password;
this.enabled = enabled;
}
public User(String email, String password, boolean enabled, Set<Role> roles) {
this.email = email;
this.password = password;
this.enabled = enabled;
this.roles = roles;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Collection<Position> getPositions() {
return positions;
}
private void setPositions(Collection<Position> positions) {
this.positions = positions;
}
public boolean addPosition(Position position) {
return positions.add(position);
}
public boolean removePosition(Position position) {
return positions.remove(position);
}
public Collection<Role> getRoles() {
return roles;
}
private void setRoles(Collection<Role> roles) {
this.roles = roles;
}
public boolean addRole(Role role) {
return roles.add(role);
}
public boolean removeRole(Role role) {
return roles.remove(role);
}
#Override
public String toString() {
return User.class + " - id: " + getId().toString() + ", email: " + getEmail();
}
}
I have read somewhere that I have to create equals() and hashCode(), so I did it in my Position Entity.
public boolean equals(Position position) {
return this.getId() == position.getId();
}
public int hashCode(){
return this.getId().hashCode() ;
}
Here are data sent by post method:
And here is my result:
My spring version: 4.1.6.RELEASE
thymeleaf-spring4 version: 2.1.4.RELEASE
thymeleaf-layout-dialect version: 1.2.8
O course I wish positions to were HashCode with one element of object Position with id = 2.
Could you help me? What I am doing wrong?
It's because you're using ${position.id} for your option value. This means spring can't work out the relationship between the id used in the value and the actual Position objects. Try just ${position} for your value and it should work:
<select th:field="*{positions}" class="form-control">
<option th:each="position : *{positions}"
th:value="${position}"
th:text="${position.name}">Wireframe
</option>
</select>
(Make sure you've implemented hashCode and equals on your Position class)
If that still doesn't work you might have to implement a Formatter for Position, to make the conversion explicit. See this example thymeleafexamples-selectmultiple.
I had similar problem that I resolved by adding Formatter class and adding Formatter to the configuration of the MVC:
#Override
protected void addFormatters(FormatterRegistry registry){
registry.addFormatter(new PositionFormater());
...
}
and Position class formatter should look something like this:
PositionFormatter:
public class PositionFormatter implements Formatter<Position>{
/** String representing null. */
private static final String NULL_REPRESENTATION = "null";
#Resource
private PositionRepository positionRepository;
public PositionFormatter() {
super();
}
#Override
public String print(Position position, Locale locale) {
if(position.equals(NULL_REPRESENTATION)){
return null;
}
try {
Position newPosition = new Position();
newPosition.setId(position.getId());
return newPosition.getId().toString();
} catch (NumberFormatException e) {
throw new RuntimeException("Failed to convert `" + position + "` to a valid id");
}
}
#Override
public Position parse(String text, Locale locale) throws ParseException {
if (text.equals(NULL_REPRESENTATION)) {
return null;
}
try {
Long id = Long.parseLong(text);
Position position = new Position();
position.setId(id);
return position;
} catch (NumberFormatException e) {
throw new RuntimeException("Failed to convert `" + text + "` to valid Position");
}
}
}
In my case, these two solved all of the problems. I have several formatters, all I do is make one and add it to the config file (WebMVCConfig in my case)
Check my original post where I resolved this problem
Thanks Guys for answering my question. You help me a lot. Unfortunately I have to disagree with you in one thing. You have shown me example with:
newPosition.setId(position.getId());
The same example was in Andrew github repository. I think that this is bad practice to use setId() method. So I will present my solution and I will wait for some comments before I will mark it as an answer.
WebMvcConfig Class
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.smartintranet")
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#PersistenceContext
private EntityManager entityManager;
// (....rest of the methods.......)
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
formatterRegistry.addFormatter(new PositionFormatter(entityManager));
}
}
PositionFormatter class
public class PositionFormatter implements Formatter<Position> {
private EntityManager entityManager;
public PositionFormatter(EntityManager entityManager) {
this.entityManager = entityManager;
}
public String print(Position position, Locale locale) {
if(position.getId() == null){
return "";
}
return position.getId().toString();
}
public Position parse(String id, Locale locale) throws ParseException {
return entityManager.getReference(Position.class, Long.parseLong(id));
}
}
employeeForm.html
<div class="col-md-6">
<label th:text="#{position}">Position</label>
<select th:field="*{position}" class="form-control">
<option th:each="position : ${allPositions}"
th:value="${position.id}"
th:text="${position.name}">Wireframe
</option>
</select>
</div>
And last one, EmployeeController Class
#Controller
public class EmployeeController extends AbstractCrudController {
// (...rest of dependency and methods....)
#Transactional
#RequestMapping(value = urlFragment + "/create", method = RequestMethod.GET)
public String createNewEmployee(Model model) {
prepareEmployeeForm(model);
return "crud/employee/create";
}
#Transactional
#RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
public String processNewEmployee(Model model, #ModelAttribute("employee") Employee employee, BindingResult result) {
if (!result.hasErrors()) {
// Look here it is important line!
entityManager.merge(employee.getUser());
}
prepareEmployeeForm(model);
return "crud/employee/create";
}
}
It is my solution. What is bad here? I think that line:
entityManager.merge(employee.getUser());
I can't use here:
userRepository.save(employee.getUser());
Because Position entity is detached, and when I use save method it runs in this situation em.persist() so I ran manually em.merge(). I know that this code is not perfect but I think that this solution is better then use setId(). I will be grateful for constructive critic.
One more time thanks Andrew and Blejzer for help without you I would not do it. I have marked yours answer as useful.
Related
I am trying to pass in every form different objects and to different entities.
It is actually works, I can see the new added values(rows) in my database, but still I keep getting the same Exception by submitting both forms separately.
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'post' available as request attribute
and
rg.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "/addPost" - line 11, col 24)
my Controller
import com.example.blog.domain.Category;
import com.example.blog.domain.Post;
import com.example.blog.service.CategoryService;
import com.example.blog.service.PostService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping("edit")
public class EditController {
private final PostService postService;
private final CategoryService categoryService;
// At the same time, I can use #Autowired
public EditController(PostService postService , CategoryService categoryService) {
this.categoryService = categoryService;
this.postService = postService;
}
//(1)
#GetMapping("/addPost")
public String showAddPost(Model model) {
model.addAttribute("post", new Post());
model.addAttribute("category" , new Category());
return "/addPost";
}
//Mapping is coming from the view page(addPost.html).
//The name of the action must be equal
#PostMapping(value = "/upload")
public String dataPost(#ModelAttribute Post post, #RequestParam(value = "action", required = true) String action){
if (action.equals("AddPost")){
postService.addPost(post);
}
return "/addPost";
}
//default : show all posts
//and shows all categories
#GetMapping
public String showAllPosts(Model model){
model.addAttribute("posts",postService.getAllPosts());
model.addAttribute("categories" , categoryService.getAllCategories());
return "index";
}
#GetMapping("/deletePost/{id}")
public String deletePostById(#PathVariable("id") Long id){
postService.deletePost(id);
return "redirect:/edit";
}
#GetMapping("/editPost/{id}")
public String editPost(#PathVariable("id") Long id,Model model){
Post post = postService.findOnePost(id);
model.addAttribute("post", post);
return "updatePost";
}
#PostMapping("/updatePostAction/{id}")
public String updateEditedPost(#ModelAttribute Post post, #PathVariable Long id){
post.setId(id);
if (post.getId() == null){
throw new NullPointerException("what the hell: " + post.getId());
}
postService.EditPost(post);
return "redirect:/edit";
}
#PostMapping(value = "/addCategory" )
public String addCategory(#ModelAttribute Category category, #RequestParam(value = "action",required = true) String action) {
if (action.equals("AddCategory")) {
categoryService.addCategory(category);
}
return "/addPost";
}
and my html file:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:action="#{/edit/upload}" th:object="${post}" method="post" >
Name:<br>
<input type="text" th:field="*{author}">
<br>
Title:<br>
<input type="text" th:field="*{title}">
<br>
Post:<br>
<textarea name="post" id="" cols="30" rows="10" th:field="*{text}"></textarea>
<br><br>
<input type="submit" name="action" value="AddPost">
</form>
<br>
<form th:action="#{/edit/addCategory}" th:object="${category}" method="post" >
New Category: <br>
<input type="text" th:field="*{name}">
<input type="submit" name="action" value="AddCategory" >
</form>
<p th:text="${post.author}"></p>
<p th:text="${post.text}"></p>
</body>
</html>
How can I solve this Exception?
EDIT:
Post class.
#Entity
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date date;
private String author;
private String title;
private String text;
public Post(String author, String title, String text) {
this.author = author;
this.title = title;
this.text = text;
}
public Post(){
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
I have a project based in Spring Web model-view-controller (MVC) framework. The version of the Spring Web model-view-controller (MVC) framework is 3.2.8.
This class
public class DeviceForm {
Device device;
List<String> selectedItems = Collections.emptyList();
public DeviceForm() {
super();
}
public Device getDevice() {
return device;
}
public void setDevice(Device device) {
this.device = device;
}
public List<String> getSelectedItems() {
return selectedItems;
}
public void setSelectedItems(List<String> selectedItems) {
this.selectedItems = selectedItems;
}
}
and this
public class Device implements java.io.Serializable {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CRITERIA")
private BaseCriteria criteria;
public BaseCriteria getCriteria() {
return criteria;
}
public void setCriteria(BaseCriteria criteria) {
this.criteria = criteria;
}
}
and this
#Entity
#Table(name = "CRITERIA")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
#SequenceGenerator(name = "seqCriteria", sequenceName = "SEQ_CRITERIA", allocationSize = 1)
public abstract class BaseCriteria {
public BaseCriteria() {
super();
}
private Long id;
private String code;
private Date adoptionDate;
private Date expirationDate;
#Transient
public abstract String getGroupKey();
#Transient
public abstract Long getGroupId();
#Transient
public abstract String getRefColumnName();
#Id
#Column(name = "ID", unique = true, nullable = true)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqCriteria")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "CODE")
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
#Column(name = "ADOPTION_DATE")
#Temporal(TemporalType.TIMESTAMP)
public Date getAdoptionDate() {
return adoptionDate;
}
public void setAdoptionDate(Date adoptionDate) {
this.adoptionDate = adoptionDate;
}
#Column(name = "EXPIRATION_DATE")
#Temporal(TemporalType.TIMESTAMP)
public Date getExpirationDate() {
return expirationDate;
}
#Transient
public boolean isExpired() {
return getExpirationDate().before(new Date());
}
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
#Override
public String toString() {
return "BaseCriteria [id=" + id + ", code=" + code + ", adoptionDate="
+ adoptionDate + ", expirationDate=" + expirationDate + "]";
}
}
and the JSP
<form:form commandName="deviceForm"
name="deviceForm"
id="deviceFormId"
method="post"
action="${contextPath}/newdesign/manage/device/${deviceForm.device.id}"
htmlEscape="yes">
<div class="col-sm-6 text-right">
<button class="btn btn-primary" type="submit">Save device</button>
</div>
</div>
<c:forEach items="${deviceForm.device.productGroup.criteria}" var="criteria">
<div class="row">
<div class="col-md-3">
<form:radiobutton path="device.criteria.id" value="${criteria.id}"/>
<label for="basic-url">Criteria:</label>
<input value="${criteria.code}" disabled="disabled" class="form-control"/>
</div>
<div class="col-md-3">
<label for="basic-url">Adoption date:</label>
<input value="<fmt:formatDate type="date" value="${criteria.adoptionDate}" />" disabled="disabled" class="form-control"/>
</div>
<div class="col-md-3">
<label for="basic-url">Expiration Date:</label>
<input value="<fmt:formatDate type="date" value="${criteria.expirationDate}" />" disabled="disabled" class="form-control"/>
</div>
</div>
</c:forEach>
</form:form>
The controller:
/**
* #throws Exception
*
*/
#RequestMapping(value = { "/newdesign/manage/device/{appId}",
"/newdesign/manage/device/{appId}/"}, method = {RequestMethod.GET})
public String viewDevicesWithStatus(
#ModelAttribute("deviceForm") DeviceForm deviceForm,
#PathVariable Long appId,
HttpServletRequest request,
Model model ) throws Exception {
Device device = manageLicenseService.getDeviceById(appId, true);
if (device.getCriteria()==null) {
device.setCriteria(device.getProductGroup().getCriteria().get(0));
}
deviceForm.setDevice(device);
fillModel (model, request, device);
return "cbViewDeviceInfo";
}
/**
* #throws Exception
*
*/
#RequestMapping(value = { "/newdesign/manage/device/{appId}",
"/newdesign/manage/device/{appId}/"}, method = {RequestMethod.POST})
public String saveDevicesWithStatus(
#ModelAttribute("deviceForm") DeviceForm deviceForm,
#PathVariable Long appId,
HttpServletRequest request,
Model model ) throws Exception {
Device device = manageLicenseService.getDeviceById(deviceForm.getDevice().getId());
if (device.getCriteria()==null) {
device.setCriteria(device.getProductGroup().getCriteria().get(0));
}
//TODO: audit
device.updateDevice(deviceForm.getDevice());
manageLicenseService.saveDevice(device);
if (device.getCriteria()==null) {
device.setCriteria(device.getProductGroup().getCriteria().get(0));
}
deviceForm.setDevice(device);
fillModel (model, request, device);
return "cbViewDeviceInfo";
}
But I got following error when I submitted the form, on GET method I got same page without error
org.springframework.beans.NullValueInNestedPathException: Invalid property 'device.criteria' of bean class [com.tdk.iot.controller.newdesign.manage.DeviceForm]: Could not instantiate property type [com.tdk.iot.domain.criteria.BaseCriteria] to auto-grow nested property path: java.lang.InstantiationException
You get the error because in your form you have this:
<form:radiobutton path="device.criteria.id" value="${criteria.id}"/>
and in your POST handler you have this:
public String saveDevicesWithStatus(#ModelAttribute("deviceForm") DeviceForm deviceForm){
}
which means that the MVC framework will try to automatically set the property
deviceForm.device.criteria.id.
Now, because there is no existing DeviceForm in any scope then it will create a new one and of course device.getCriteria() returns null,
hence the exception.
You may think that the DeviceForm you created and populated in the GET handler will be used however Spring MVC is stateless so you
would need to store it in Session scope between requests for it to be re-used or otherwise rework your logic.
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib-method-args
.... Given the above example where can theinstance come from? There
are several options.....[in the absence of any other option] It may
be instantiated using its default constructor
A better approach however is to change your form to be as below:
<form:radiobutton path="device.criteria" value="${criteria.id}"/>
and register a converter that would convert the submitted parameter and bind the corresponding entity instance.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert
#Component
public class StringToCriteriaConverter implements Converter<String, BaseCriteria> {
#Autowired
private CriteriaService service;
//source is the ID passed by the page
public BaseCriteria convert(String source) {
// lookup and return item with corresponding ID from the database
}
}
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;
}
}
In my spring project, Hibernate is used to manage the access to the database. For this, I have the tradicional schema using Entity and Dao classes, which are used by my service classes. am facing problems when I need remove a row from some table in my database. For instance, follow the code below:
remove.jsp
<c:url value="/usuario/listagem" var="listagem"/>
<c:url value="/permissao/remove" var="removePermissao"/>
<form class="form" role="form" action="${removePermissao}" method="post">
<div class="alert alert-warning" id="pergunta">
Tem certeza que quer excluir o grupo de permissões <strong><c:out value="${permissao.nome}"/></strong>?
<input type="hidden" name="id" value="<c:out value="${permissao.id}"/>">
<button type="submit" class="btn btn-lg btn-link"><span class="glyphicon glyphicon-ok"></span></button>
<button type="button" class="btn btn-lg btn-link link" data-action="${listagem}"><span class="glyphicon glyphicon-remove"></span></button>
</div>
</form>
<div id="yes" class="alert alert-success" style="display: none;">
<strong>Pronto!</strong> Permissão excluida com sucesso.
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
</div>
<div id="not" class="alert alert-danger" style="display: none;">
<strong>Erro!</strong> Não foi possivel excluir a permissão.
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
</div>
method remove() from controller
#RequestMapping(value="remove", method=RequestMethod.POST)
#ResponseBody
public String remove(HttpServletRequest request, HttpServletResponse response) {
if(permissao.remove(request, response))
return "yes";
else
return "not";
}
method remove() from service class
#PreAuthorize("hasPermission(#user, 'altera_permissao')")
#Transactional
public boolean altera(HttpServletRequest request, HttpServletResponse response) {
String id_usuario = request.getParameter("usuario");
String id_permissao = request.getParameter("grupo");
String possui = request.getParameter("possui");
if(possui.equals("yes")) {
Usuario user = usuario.findById(Integer.valueOf(id_usuario).intValue());
user.getAutorizacao().add(grupo_permissao.findById(Integer.valueOf(id_permissao).intValue()));
return usuario.merge(user);
}
else {
Usuario user = usuario.findById(Integer.valueOf(id_usuario).intValue());
int max = user.getAutorizacao().size();
for(int i=0; i<max; i++) {
if(user.getAutorizacao().get(i).equals(grupo_permissao.findById(Integer.valueOf(id_permissao).intValue()))) {
user.getAutorizacao().set(i, null);
}
}
return usuario.merge(user);
}
}
#PreAuthorize("hasPermission(#user, 'remove_permissao')")
#Transactional
public boolean remove(HttpServletRequest request, HttpServletResponse response) {
String id = request.getParameter("id");
GrupoPermissao novo = grupo_permissao.findById(Integer.valueOf(id).intValue());
novo.setPermissao(null);
return grupo_permissao.remove(novo);
}
In the first method, altera(...), the second block of code from if...else should remove a permission fromthe user. The second method remove a role created by the user (roles in my project are a set of permissions).
The problem in first method is that when I try remove a row, a new one is inserted instead. In the second method, I am not able to remove the element because the system return an error of violation foreign key.
Anyone can help with some information of jow I should modify this method to accomplish the desired function?
ps.: my entity class related to the example above is:
Permissao.java
#Entity
#Table(name="permission")
public class Permissao {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#Column(name="nome")
private String nome;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
}
GrupoPermissao.java
#Entity
#Table(name="role")
public class GrupoPermissao {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#Column(name="nome")
private String nome;
#ManyToMany
#JoinTable(name="role_permissions", joinColumns={#JoinColumn(name="fk_role")}, inverseJoinColumns={#JoinColumn(name="fk_permission")})
#LazyCollection(LazyCollectionOption.FALSE)
private List<Permissao> permissao = new ArrayList<Permissao>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public List<Permissao> getPermissao() {
return permissao;
}
public void setPermissao(List<Permissao> permissao) {
this.permissao = permissao;
}
}
UPDATE 2
My final code is this:
#PreAuthorize("hasPermission(#user, 'altera_permissao')")
#Transactional
public boolean altera(HttpServletRequest request, HttpServletResponse response) {
String id_usuario = request.getParameter("usuario");
String id_permissao = request.getParameter("grupo");
String possui = request.getParameter("possui");
if(possui.equals("not")) {
Usuario user = usuario.findById(Integer.valueOf(id_usuario).intValue());
user.getAutorizacao().add(grupo_permissao.findById(Integer.valueOf(id_permissao).intValue()));
return usuario.merge(user);
}
else {
Usuario user = usuario.findById(Integer.valueOf(id_usuario).intValue());
int max = user.getAutorizacao().size();
int index[] = new int[max];
for(int i=0; i<max; i++)
index[i] = -1;
for(int i=0; i<max; i++) {
String p_user = user.getAutorizacao().get(i).getNome();
String p_comp = grupo_permissao.findById(Integer.valueOf(id_permissao).intValue()).getNome();
System.out.println(i+"->"+p_user+"=="+p_comp);
if(p_user.equals(p_comp)) {
index[i] = i;
}
}
for(int i=0; i<max; i++)
if(index[i] >= 0)
user.getAutorizacao().remove(i);
return usuario.merge(user);
}
}
Essentially you need to call session.getCurrentSession().delete(entity); to remove the entity using hibernate.
Or you can create a cutomer query using createQuery.
This is a great example of how to remove an object from table using hibernate:
http://examples.javacodegeeks.com/enterprise-java/hibernate/delete-persistent-object-with-hibernate/
EDIT 1:
Based on extra information provided where you want to remove permission relationship from user
then code you need to insert into either controller or pojo can be following:
User user = // allocate your user;
Permissao permissao = // allocate the permission you wish to remove
user.setPermissao(user.getPermissao().remove(permissaio);
then save the user object through saveOrUpdate() or Update() function.
This will remove the relationship to the permissao from the user. Keeps the user and permission in database.
I try to display a part of gallery's photo=> this is result when i did the search action. This result have avatar like a picture and infomation like username or email.
I create Photo.java like child of Users.java in relationship #ManytoOne
Here is my code :
Photo.java----
#Entity
public class Photo extends Model{
#Id
public Long id;
public String path;
#ManyToOne
#JoinColumn(name = "user_id")
public Users user;
}
Users.java-----
#Entity
public class Users extends Model{
#Id
public Long id;
#Constraints.Required
public String username;
#Constraints.Required
public String email;
#Constraints.Required
public String password;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
public List<Photo> photo = new ArrayList<Photo>();
public Users(){}
public Users(String username,String email,String password){
this.username=username;
this.email=email;
this.password=password;
}
}
Search.java -----
public static Result search(){
DynamicForm form = form().bindFromRequest();
String name = form.get("name");
Finder<Long, Users> find = new Finder<Long, Users>(Long.class, Users.class);
List<Users> users = find.where().like("username", '%'+ name +'%').findList();
if (form.get("name")=="" || users.isEmpty() || users==null){
return ok(search_again.render());
}
else{
return ok (search_result.render(users));
}
}
search_result.scala.html----
#(users : List[Users])
#main(nav= "search"){
<h3>Result</h3>
<input class="button" type="button" value="Back to Search">
<input class="button" type="button" value="Back to Home">
<p>Found #users.size() result(s) : </p>
<div class="sresult">
#for(user <- users){
<div id="sresult">
<div id="haha"><img src="#routes.Assets.at("upload/"+user.photo.path)"></div>
//Error here. Why "user.photo.path" not working ?
<p>
#user.username</a></br>
#user.password</a></br>
#user.email</a>
</p>
</div>
}
</div>
}
Why "user.photo.path" not working ? any ideal in my case ?