Est-il possible de mettre à jour seulement un sous-ensemble d'attributs sur une entité en utilisant Spring MVC avec JPA?

Je suis travailler avec Spring Roo, à l'aide de Spring MVC et JPA pour la persistance avec une base de données MySQL. Je suis très nouveau à Spring MVC et Java en général, mais ont travaillé avec CakePHP et de Rails.

J'ai un User entité qui contient des informations personnelles en plus du mot de passe. Quelque chose comme ceci (à l'exclusion de beaucoup de Roo-généré des fonctionnalités supplémentaires .aj fichiers):

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
    ...
}

Puis-je avoir une action edit dans mon User contrôleur que j'ai créé à la suite de conventions de Roo auto-généré échafaudage:

@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";
}

Et un JSPX vue de rendre la forme, de nouveau Roo 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>

Je ne pas souhaitez que le formulaire pour mettre à jour le mot de passe, les champs (nom, prénom, et adresse).

L'action de mise à jour, en suivant de nouveau 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";
}

L'utilisateur de l'objet est mise à jour parfaitement, mais le problème est qu'il écrase le champ mot de passe avec la valeur null, car il n'est pas fourni dans la forme, et donc la valeur null dans l'Utilisateur de l'objet passé à soumettre le formulaire de demande de gestionnaire. Le problème n'apparait pas avec le Roo généré échafaudage parce qu'ils fournissent des entrées d'un formulaire pour toutes les colonnes. Afin que je puisse l'ajouter comme un champ caché, mais ce n'est pas une bonne idée. Et j'ai le sentiment il ya une bien meilleure façon de le faire...


TL;DR Comment puis-je mettre à jour uniquement les attributs d'entité sous une forme sans écraser les autres attributs?

En d'autres termes, comment puis-je faire Spring/JPA générer le SQL

UPDATE user SET firstname=?, lastname=?, address=?

au lieu de

UPDATE user SET firstname=?, lastname=?, address=?, password=?

Exemples de Code serait fantastique depuis que je suis nouveau à tout cela 🙂


Merci!


Mise à JOUR: j'ai été capable de le faire fonctionner à l'aide de yglodt de la suggestion, ajouter la méthode suivante pour mon modèle d'Utilisateur:

@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();
}

et l'appel que de mon contrôleur de l'action à la place de l'utilisateur.merge():

user.mergeWithExistingAndUpdate();

source d'informationauteur Eric Freese