我正在使用Spring MVC和JPA(Hibernate)。
问题是,当我尝试编辑用户时,不会发回id(null),因此存储库save方法会生成一个新的User对象而不是更新它。
我的User类有MappedSuperClass
BaseEntity
。映射实现如下:
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Version
private Long version;
protected BaseEntity() {
id = null;
}
public Long getId() {
return id;
}
public void setId(Long id){
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
@Entity
public class User extends BaseEntity implements UserDetails {
@NotNull
@Size(min = 5, max = 30)
private String name;
private String username;
private LocalDate dateOfBirth;
private String address;
@JsonIgnore
private String email;
@JsonIgnore
private String password;
private String barcode;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "role_id")
@JsonIgnore
private Role role;
@JoinTable(name = "loan", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "book_id"))
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Book> loanedBooks;
private boolean enabled;
public User() {
super();
}
public User(String name, String username, String email, String password, Role role, boolean enabled, LocalDate dateOfBirth) {
this();
this.name = name;
this.username = username;
this.email = email;
setPassword(password);
this.role = role;
this.enabled = enabled;
this.dateOfBirth = dateOfBirth;
loanedBooks = new HashSet<>();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(role.getName()));
return authorities;
}
public void loanBook(Book book) throws BookNotAvailableException{
if (book.isAvailable()) {
loanedBooks.add(book);
} else {
throw new BookNotAvailableException("Book is not available right now");
}
}
public void returnBook(Book book) {
for (Book returnBook : loanedBooks) {
if (returnBook.getBarcode().equals(book.getBarcode()));
loanedBooks.remove(returnBook);
}
}
public Set<Book> getLoanedBooks() {
return loanedBooks;
}
public void setLoanedBooks(Set<Book> loanedBooks) {
this.loanedBooks = loanedBooks;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String getPassword() {
return password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode = barcode;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Role getRole() {
return role;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setRole(Role role) {
this.role = role;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(LocalDate dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
@Controller
public class UserController {
@Autowired
UserService userService;
@GetMapping(path = "/users")
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_LIBRARIAN')")
public String index(Model model) {
return "user/index";
}
// Form for editing an existing user
@RequestMapping("users/{userId}/edit")
public String formEditUser(@PathVariable Long userId, Model model) {
// TODO: Add model attributes needed for new form
if (!model.containsAttribute("user")) {
model.addAttribute("user", userService.findById(userId));
}
model.addAttribute("action", String.format("/users/%s", userId));
model.addAttribute("heading", "Edit User");
model.addAttribute("submit", "Update");
return "user/form";
}
// Update an existing user
@RequestMapping(value = "/users/{userId}", method = RequestMethod.POST)
public String updateUser(@Valid User user, BindingResult result, RedirectAttributes redirectAttributes) {
// Update user if valid data was received
if (result.hasErrors()) {
// Include validation errors upon redirect
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.user", result);
// Add user if invalid data was received
redirectAttributes.addFlashAttribute("user", user);
// Redirect back to the form
return String.format("redirect:/users/%s/edit", user.getId());
}
userService.save(user);
redirectAttributes.addFlashAttribute("flash", new FlashMessage("User successfully updated!", FlashMessage.Status.SUCCESS));
//Redirect browser to /users
return "redirect:/users";
}
// Form for adding a new user
@RequestMapping(value = "users/add", method = RequestMethod.GET)
public String formNewUser(Model model) {
//..
}
// Add a User
@RequestMapping(value = "/users", method = RequestMethod.POST)
public String addUser(@Valid User user, BindingResult result, RedirectAttributes redirectAttributes) {
// Add user if valid data was received
if (result.hasErrors()) {
// Include validation errors upon redirect
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.user", result);
// Add user if invalid data was received
redirectAttributes.addFlashAttribute("user", user);
// Redirect back to the form
return "redirect:/users/add";
}
userService.save(user);
redirectAttributes.addFlashAttribute("flash", new FlashMessage("User successfully added!", FlashMessage.Status.SUCCESS));
return "redirect:/users";
}
// Index for user search and listing
@GetMapping(path = "/users/search")
public String userSearch(@RequestParam("name_or_barcode") String nameOrBarcode,Model model) {
//..
}
// Single user page
@RequestMapping("/users/{userId}")
public String user(@PathVariable Long userId, Model model) {
User user = userService.findById(userId);
model.addAttribute("user", user);
return "user/details";
}
// Delete an existing user
@RequestMapping(value = "/users/{userId}/delete", method = RequestMethod.POST)
public String deleteUser(@PathVariable Long userId, RedirectAttributes redirectAttributes) {
User user = userService.findById(userId);
if(user.getLoanedBooks().size() > 0) {
redirectAttributes.addFlashAttribute("flash", new FlashMessage("User cannot be deleted because he/she has borrowed books",FlashMessage.Status.FAILURE));
return String.format("redirect:/users/%s/edit", userId);
}
userService.delete(user);
redirectAttributes.addFlashAttribute("flash", new FlashMessage("User deleted", FlashMessage.Status.SUCCESS));
return "redirect:/users";
}
}
注意:隐藏的id字段就在那里。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="layout :: head"></head>
<body>
<div th:replace="layout :: nav"></div>
<div th:replace="layout :: flash"></div>
<div class="container">
<form th:action="@{${action}}" method="post" th:object="${user}">
<input type="hidden" th:field="*{enabled}" />
<input type="hidden" th:field="*{role}" />
<input type="hidden" th:field="*{id}" />
<div class="row">
<div class="col s12">
<h2 th:text="${heading}">New User</h2>
</div>
</div>
<div class="divider"></div>
<div class="row">
<div class="col s12 l8" th:classappend="${#fields.hasErrors('name')} ? 'error' : ''">
<input type="text" th:field="*{name}" placeholder="Full Name"/>
<div class="error-message" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
</div>
</div>
<div class="row">
<div class="col s12 l8">
<button th:text="${submit}" type="submit" class="button">Add</button>
<a th:href="@{/users}" class="button">Cancel</a>
</div>
</div>
</form>
<div class="row delete" th:if="${user.id != null}">
<div class="col s12 l8">
<form th:action="@{|/users/${user.id}/delete|}" method="post">
<button type="submit" class="button">Delete</button>
</form>
</div>
</div>
</div>
<div th:replace="layout :: scripts"></div>
</body>
</html>
有趣的是,启用的字段值返回1,这很好。
答案 0 :(得分:2)
字段绑定看起来正确,但必须绑定version
属性才能执行更新;否则,执行创建。考虑添加以下表单字段绑定:
<input type="hidden" th:field="*{version}" />
此链接可能有用:Updating object through CRUD-Repository`s save method changes it's ID. | Treehouse Community。
希望这有帮助。