I'm working with Spring Roo, using Spring MVC and JPA for persistence with a MySQL database. I'm very new to Spring MVC and Java in general but have worked with CakePHP and Rails.
I have a User
entity that contains personal details in addition to a password. Something like this (excluding a lot of Roo-generated functionality in additional .aj files):
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
private Long id;
@Column(name = "PASSWORD", length = 32)
private String password;
@Column(name = "FIRST_NAME", length = 25)
private String firstName;
@Column(name = "LAST_NAME", length = 25)
private String lastName;
@Column(name = "ADDRESS", length = 255)
private String address;
// The appropriate getters and setters
...
}
Then I have an edit action in my User
controller that I created following conventions from Roo's auto-generated scaffolding:
@RequestMapping(value="/edit", method = RequestMethod.GET)
public String editForm(Model uiModel) {
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
uiModel.addAttribute("user", User.findUserByUsername(username).getSingleResult());
return "account/edit";
}
And a JSPX view to render the form, again following Roo's conventions:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
<jsp:directive.page contentType="text/html;charset=UTF-8"/>
<jsp:output omit-xml-declaration="yes"/>
<form:update id="" label="Personal Details" modelAttribute="user" path="/account" versionField="none">
<field:input id="" field="firstName" label="First Name" />
<field:input id="" field="lastName" label="Last Name" />
<field:textarea id="" field="address" label="Street Address" />
</form:update>
</div>
I do not want the form to update the password, just the provided fields (first name, last name, and address).
The update action, again following Roo convention:
@RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String edit(@Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("user", user);
return "account/edit";
}
uiModel.asMap().clear();
user.merge();
return "redirect:/account";
}
The user object is updated perfectly, but the problem is that it overwrites the password field with null because it's not provided as an input in the form, and thus set to null in the User object passed to the form submit request handler. The problem doesn't show up with the Roo generated scaffolding because they provide form inputs for all of the columns. So I could add it as a hidden field but that doesn't sound like a good idea. And I get the feeling there's a much better way to do it...
TL;DR How can I update only the entity attributes provided in a form without overwriting the other attributes?
In other words, how can I make Spring/JPA generate the SQL
UPDATE user SET firstname=?, lastname=?, address=?
instead of
UPDATE user SET firstname=?, lastname=?, address=?, password=?
Code samples would be fantastic since I'm new to all of this :)
Thank you!
UPDATE: I was able to make it work using yglodt's suggestion, adding the following method to my User model:
@Transactional
public void mergeWithExistingAndUpdate() {
final User existingUser = User.findUser(this.getId());
existingUser.setFirstName(this.getFirstName());
existingUser.setLastName(this.getLastName());
existingUser.setAddress(this.getAddress());
existingUser.flush();
}
and calling that from my controller action instead of user.merge():
user.mergeWithExistingAndUpdate();