web-dev-qa-db-fra.com

Spring JpaRepository - Détacher et attacher une entité

J'utilise Spring Boot et hibernate over jpa. J'utilise l'interface JpaRepository pour implémenter mes référentiels. Comme avec UserRepository suivant

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

Je veux réussir à suivre

  1. Chargez une entité utilisateur.
  2. Modifiez l'état de l'objet entité, par exemple user.setName ("foo")
  3. Effectuez un appel de service Web système externe. Enregistrer le résultat de l'appel dans DB
  4. Uniquement en cas de réponse réussie de cet appel de service Web, enregistrez le nouvel état de l'utilisateur dans le référentiel.

Toutes les étapes ci-dessus ne se produisent pas dans une transaction, c'est-à-dire que l'appel de service externe est hors transaction.

Lorsque j'enregistre le résultat de mon service Web dans DB via son référentiel, mes modifications dans l'entité utilisateur sont également enregistrées. Selon ma compréhension, cela est dû au vidage du contexte de persistance sous-jacent à l'étape # 3. Après quelques recherches sur Google, je pense que je peux atteindre mon objectif, si je peux détacher mon entité utilisateur à la première étape et la rattacher à l'étape 4. Veuillez confirmer si ma compréhension est correcte et comment y parvenir? Il n'y a pas de méthode dans l'interface JpaRepository pour détacher une entité.

Voici le code pour illustrer

public void updateUser(int id, String name, int changeReqId){
    User mUser = userRepository.findOne(id); //1
    mUser.setName(name); //2

    ChangeRequest cr = changeRequestRepository.findOne(changeReqId);
    ChangeResponse rs = userWebService.updateDetails(mUser); //3

    if(rs.isAccepted()){
        userRepository.saveAndFlush(mUser); //4
    }

    cr.setResponseCode(rs.getCode());
    changeRequestRepository.saveAndFlush(cr); //this call also saves the changes at step 2
}

Merci

22
amique

Si vous utilisez JPA 2.0, vous pouvez utiliser EntityManager # detach () pour détacher une seule entité du contexte de persistance. De plus, Hibernate a un Session # evict () qui sert le même but.

Puisque JpaRepository ne fournit pas cette fonctionnalité elle-même, vous pouvez ajouter une implémentation personnalisée , quelque chose comme ceci

public interface UserRepositoryCustom {
    ...
   void detachUser(User u);
    ...
}

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

@Repository
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
    ...
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void detachUser(User u) {
        entityManager.detach(u);
    }
    ...
}

Je n'ai pas essayé ce code, mais vous devriez pouvoir le faire fonctionner. Vous pouvez même essayer d'obtenir une prise sur EntityManager dans votre classe de service (où updateUser() est) avec @PersistenceContext, Et éviter l'effort d'ajouter une implémentation personnalisée au référentiel.

47
Predrag Maric

entityManager.clear() déconnectera tous les objets JPA, ce qui pourrait ne pas être une solution appropriée dans tous les cas, si vous avez d'autres objets que vous prévoyez de garder connectés.

clair

/**
 * Clear the persistence context, causing all managed
 * entities to become detached. Changes made to entities that
 * have not been flushed to the database will not be
 * persisted.
 */
public void clear();

entityManager.detach(entity); Supprimer l'entité donnée du contexte de persistance

détacher

/**
 * Remove the given entity from the persistence context, causing
 * a managed entity to become detached.  Unflushed changes made
 * to the entity if any (including removal of the entity),
 * will not be synchronized to the database.  Entities which
 * previously referenced the detached entity will continue to
 * reference it.
 * @param entity  entity instance
 * @throws IllegalArgumentException if the instance is not an
 *         entity
 * @since Java Persistence 2.0
 */
public void detach(Object entity);
4
Xstian