comment utiliser les em.merge() pour insérer OU de mettre à jour pour les entités jpa si la clé primaire est générée par la base de données?
J'ai une entité JPA comme ceci:
@Entity
@Table(name = "category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Basic(optional = false)
@Column(name = "name")
private String name;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
private Collection<ItemCategory> itemCategoryCollection;
//...
}
Utiliser Mysql comme base de données. "nom" est conçu comme une clé unique. Utiliser Hibernate comme fournisseur JPA.
Le problème de l'utilisation de méthode de fusion est que parce que pk est généré par la db, donc si l'enregistrement existe déjà (le nom est déjà là) puis Hibernate va essayer de l'insérer dans la base de données et je vais avoir une clé unique contraindre à l'exception de violation et de ne pas faire la mise à jour . Ce que quelqu'un a une bonne pratique pour gérer cela? Merci!!!!
P. S: ma solution est comme ceci:
public void save(Category entity) {
Category existingEntity = this.find(entity.getName());
if (existingEntity == null) {
em.persist(entity);
//code to commit ...
} else {
entity.setId(existingEntity.getId());
em.merge(entity);
//code to commit ...
}
}
public Category find(String categoryName) {
try {
return (Category) getEm().createNamedQuery("Category.findByName").
setParameter("name", categoryName).getSingleResult();
} catch (NoResultException e) {
return null;
}
}
OriginalL'auteur Bobo | 2010-10-22
Vous devez vous connecter pour publier un commentaire.
Si vous êtes à l'aide des identifiants générés ou pas, c'est de l'OMI hors de propos. Le problème ici est que vous voulez mettre en œuvre un "upsert" sur certains de clé unique autre que la PK et de la JPA n'a pas vraiment d'apporter son soutien (
merge
s'appuie sur l'identité de base de données).Vous avez donc autant que je sache, les 2 options.
Soit effectuer une INSERTION de la première et de mettre en œuvre certaines mécanisme de nouvelle tentative en cas d'échec à cause d'une violation de contrainte d'unicité et puis de trouver et de mettre à jour les enregistrements existants (à l'aide d'une nouvelle entité gestionnaire).
Ou, SÉLECTIONNER un premier, puis d'insérer ou de mettre à jour en fonction du résultat de la sélection (c'est ce que vous avez fait). Cela fonctionne, mais n'est pas garanti à 100% que vous pouvez avoir une condition de concurrence entre les deux threads simultanés (ils ne pourraient pas trouver un enregistrement pour un nom de catégorie et essayez d'insérer, en parallèle, le plus lent thread échouera). Si c'est peu probable, il pourrait être une solution acceptable.
Mise à jour: Il pourrait y avoir une 3ème option de bonus si vous n'avez pas l'esprit à l'aide d'une base MySQL fonctionnalité exclusive, voir 12.2.5.3. INSÉRER ... SUR un DOUBLE de la CLÉ de mise à JOUR de Syntaxe. Jamais testé avec JPA.
Bien, 1. veuillez expliquer me pourquoi utiliser des identifiants générés est pertinente pour la question 2. voyez-vous des solutions plus (en dehors de l'utilisation de la base de données des caractéristiques propriétaires).
Voulez-vous dire, le sens de l'OMI et autant que je sache? Si c'est ce que tu veux dire, ici, vous allez: OMI = À Mon Avis, et autant que je sache = autant Que je sache.
Pour la 3ème option, je sais que cela va fonctionner, utilisé avant. Mais le truc, c'est qu'il m'oblige à écrire natif de requêtes de ce que je suis en train de les éviter. Je préfèrerais utiliser la norme EntityManager méthodes(persist, merge, etc.) pour accomplir les opérations CRUD.
OriginalL'auteur Pascal Thivent
Je n'ai pas vu cette mentionné avant, donc j'aimerais juste ajouter une solution qui évite de faire plusieurs requêtes. Gestion des versions.
Normalement utilisé comme un moyen simple de vérifier si un enregistrement en cours de mise à jour a perdu de leur charme dans le verrouillage optimiste scénario, les colonnes annoté avec @Version peut également être utilisé pour vérifier si un enregistrement est persistante (présent dans la base de données) ou non.
Tout cela peut paraître compliqué, mais il ne l'est pas vraiment. Ce qu'il se résume à une colonne supplémentaire sur le disque, dont la valeur change à chaque mise à jour. Nous définissons une colonne supplémentaire de la version dans notre base de données comme ceci:
Et marquez le champ correspondant dans notre classe Java avec
@Version
comme ceci:L' @Version annotation faire JPA utilisation de cette colonne avec verrouillage optimiste en l'incluant comme condition à toute mise à jour des déclarations, comme ceci:
(JPA fait automatiquement, pas besoin de l'écrire vous-même)
Puis, ensuite, il vérifie si un enregistrement a été mis à jour (comme prévu) ou pas (dans ce cas, l'objet a été vicié).
Nous devons faire en sorte que personne ne peut définir le champ version ou il serait gâcher le verrouillage optimiste, mais on peut faire un getter sur
version
si nous voulons. On peut aussi utiliser le champ de version dans une méthodeisPersistent
qui permettra de vérifier si l'enregistrement est dans la DB déjà ou non, sans jamais faire une requête:Enfin, nous pouvons utiliser cette méthode dans notre
insertOrUpdate
méthode:id
colonne, mais si elle est générée par la DB (ce qui est fait dans l'exemple donné). Je suis à l'aide de cette tecnique dans un projet dans lequel les Id ne sont pas générés par la DB et il est bien pratique.OriginalL'auteur Stijn de Witt