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
Vous devez vous connecter pour publier un commentaire.
J'ai l'habitude de résoudre ce dans la couche de service.
Vous pouvez lire l'entité que vous souhaitez mettre à jour à partir de la DB, et de remplacer les attributs qui vous obtenez à partir de votre forme.
De cette façon, vous modifiez les attributs que vous souhaitez.
Exemple de Code:
EDIT 1
Il est en fait un Printemps de la façon de résoudre ce problème, l'utilisation d'
@SessionAttributes
voir cette réponse:https://stackoverflow.com/a/3675919/272180
Je n'ai pas encore tester, mais il semble prometteur.
EDIT 2
Finalement je l'ai testé et il fonctionne comme prévu.
Il y a néanmoins une chose qui peut vous faire tourner vos pieds:
Si vous ouvrez plusieurs onglets avec la même forme, l'ouverture du dernier onglet remplace le
sessionAttribute
des autres, et sur soumettre, peut potentiellement endommager vos données. Il y a une solution dans ce post de blog: http://marty-java-dev.blogspot.com/2010/09/spring-3-session-level-model-attributes.htmlMais à la fin, si vous n'avez jamais ouvrir plusieurs onglets pour le montage, vous n'aurez pas de problème de toute façon.
Cet article explique dans beaucoup de détails à votre question, mais je vais résumer ici.
Si vous ne voulez pas mettre à jour un attribut particulier, vous pouvez le marquer avec
updatable=false
:Une fois que vous chargez une entité et de le modifier, aussi longtemps que la présente
Session
ouEntityManager
est ouvert, Hibernate peut suivre les changements à travers le sale mécanisme de contrôle de. Ensuite, pendant le rinçageunSQL
mise à jour sera exécutée.Si vous n'aimez pas que toutes les colonnes sont inclus dans le
UPDATE
déclaration, vous pouvez utiliser la mise à jour dynamique:Ensuite, seulement la modification de colonnes seront inclus dans le
UPDATE
déclaration.Si votre fournisseur de persistance est en veille prolongée, utiliser la mise en veille prolongée-spécifique annotation:
@DynamicUpdate
sur l'entité: