web-dev-qa-db-fra.com

Comment utiliser hibernate @ DynamicsUpdate avec spring data jpa?

J'utilise spring data-jpa. Je veux mettre à jour une seule colonne.

Mon référentiel est;

public interface UserRepository extends JpaRepository<User,Long> {
}

mon service est;

public User save(User user) {
    return userRepository.save(user);
}

mon entité;

@Entity
@DynamicUpdate(true)
public class User implements Serializable {
    // column definitions, etc.
}

Comment puis-je mettre à jour une seule colonne dans User?

4
Ravi Developer

Votre problème est dû au passage d'une entité User entièrement nouvelle. Hibernate ne peut donc pas utiliser une version mise en cache qui a déjà été extraite de la base de données et décider des colonnes à mettre à jour de manière dynamique. 

Donc, essayez de faire ce qui suit pour confirmer le comportement correct de @DynamicUpdate ;

En service;

@Transactional
public User save(User newUser) {
    User currentUser = userRepository.get(newUser.getId());
    // handle merging of updated columns onto currentUser manually or via some mapping tool
    currentUser.setName(newUser.getName());
    return userRepository.save(currentUser);
}

Avec la logique ci-dessus, avec l'annotation de mise à jour dynamique, vous devriez pouvoir voir la mise à jour de la colonne de nom uniquement, sauf si un audit est activé, ou si vous utilisez la colonne @Version 'ed. 

Si les colonnes mises à jour sont toujours identiques, il est préférable d'utiliser updatable = false pour les colonnes non ciblées pour la mise à jour dans leurs définitions @Column, car utiliser @DynamicUpdate est très inefficace car il génère chaque sql de nouveau, n'utilisant jamais les sqls mis en cache. Mais méfiez-vous de cette fonctionnalité, car vous ne pourrez absolument pas mettre à jour ces colonnes. 

Je ne recommande pas d'utiliser @Query sauf si vous avez un cas où JPA/Hibernate natif est insuffisant, mais si vous avez un cas d'utilisation pour mettre à jour un ensemble de colonnes cible, c'est le meilleur choix, et le plus efficace.

Si les colonnes mises à jour sont nombreuses et peuvent varier considérablement, définissez une logique de mappage manuelle entre newUser et currentUser ou utilisez des outils de mappage tels que Orika , j'ai une structure automatisée similaire dans laquelle des centaines d'entités sont corrigées mappeurs, ce qui permet une manière extrêmement générique de gérer de nombreuses opérations CRUD.

3
buræquete

Tout d’abord, je veux expliquer pourquoi @DynamicUpdate ne fonctionne pas pour vous. Je devrais donc remarquer le fonctionnement de @DynamicUpdate:

L'annotation @DynamicUpdate sert à spécifier Que l'instruction UPDATE SQL doit être générée chaque fois qu'une entité Est modifiée. Par défaut, Hibernate utilise une instruction UPDATE en cache qui Définit toutes les colonnes de la table. Lorsque l'entité est annotée avec l'annotation @DynamicUpdate, PreparedStatement n'inclura Que les colonnes dont les valeurs ont été modifiées. ( plus de détails )

Par exemple, supposons que User possède une propriété appelée name.

Donc, si pour la première fois vous avez enregistré un utilisateur avec un nom défini, vous transmettez maintenant null car la valeur @DynamicUpdate l’acceptera comme un changement et vous essayez de mettre à jour le nom à null.

Première solution:

En tant que première solution, si vous voulez que @DynamicUpdate fonctionne, vous devez d'abord renseigner toutes les autres propriétés User avec d'anciennes valeurs, puis vous pouvez l'enregistrer.

Pros: La requête SQL générée ne fait que mettre à jour la propriété souhaitée.

Cont: Hibernate doit générer la chaîne SQL correspondante à chaque fois, ce qui entraîne un coût de performance du côté Hibernate.

Deuxième solution:

Vous pouvez mettre à jour User avec une requête personnalisée:

@Modifying
@Query("UPDATE User SET name=(:name) WHERE id=(:id)")
public void updateName(@Param("name")String name, @Param("id")Long id);

Troisième solution:

Comme dernière solution, je peux vous suggérer d'utiliser updatable = false. Cela remplira la propriété au tout premier moment où l'entité insérée.

  @Column(name = "create_date", nullable = false, updatable = false)
    private Instant createDate;
1
Spara