web-dev-qa-db-fra.com

Spring Data: Override save method

J'envisage des données de printemps pour un projet. Est-il possible de remplacer la méthode de sauvegarde générée par défaut? Et si oui, comment?

33
beginner_

Cela n'a pas fonctionné correctement, j'ai donc mis ma logique requise dans une classe de service et laissé la méthode de sauvegarde des référentiels intacte.

6
beginner_

Créez simplement votre interface personnalisée comme d'habitude et déclarez-y les méthodes que vous souhaitez remplacer avec la même signature que celle exposée par CrudRepository (ou JpaRepository, etc.). Supposons que vous ayez une entité MyEntity et un référentiel MyEntityRepository et que vous souhaitiez remplacer la méthode save générée automatiquement par défaut de MyEntityRepository qui prend une seule instance d'entité , puis définissez:

public interface MyEntityRepositoryCustom {
  <S extends MyEntity> S save(S entity);
}

et implémentez cette méthode comme vous le souhaitez dans votre MyEntityRepositoryImpl, comme d'habitude:

@Transactional
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
  public <S extends MyEntity> S save(S entity) {
    // your implementation
  }
}

Et puis, comme d'habitude, laissez MyEntityRepository implémenter MyEntityRepositoryCustom.

Ce faisant, Spring Data JPA appellera la méthode save de votre MyEntityRepositoryImpl au lieu de l'implémentation par défaut. Au moins, cela fonctionne pour moi avec la méthode delete dans Spring Data JPA 1.7.2.

28
Mauro Molinari

Pour remplacer la méthode de sauvegarde générée par défaut, vous devez utiliser l'agrégation de l'implémentation du référentiel Spring Data dans votre propre implémentation de référentiel personnalisé.

Interface de référentiel:

public interface UserRepository extends CrudRepository<User, String>{

}

L'implémentation de votre référentiel:

@Repository("customUserRepository")
public class CustomUserRepository implements UserRepository {

    @Autowired
    @Qualifier("userRepository") // inject Spring implementation here
    private UserRepository userRepository;

    public User save(User user) {
        User user = userRepository.save(entity);
        // Your custom code goes here
        return user;
    }

    // Delegate other methods here ...

    @Override
    public User findOne(String s) {
        return userRepository.findOne(s);
    }
}

Utilisez ensuite votre implémentation personnalisée dans votre service:

@Autowired
@Qualifier("customUserRepository")
private UserRepository userRepository;
8
Ignat Sachivko

Je suppose que vous étendez SimpleJpaRepository:

public class **CustomSimpleJpaRepository** extends SimpleJpaRepository {

@Transactional
public <S extends T> S save(S entity) { //do what you want instead }
}

Assurez-vous ensuite que ceci est utilisé à la place du SimpleJpaRepository par défaut en étendant:

public class CustomJpaRepositoryFactory extends JpaRepositoryFactory {

    protected <T, ID extends Serializable> JpaRepository<?, ?> getTargetRepository(RepositoryMetadata metadata, EntityManager entityManager) {

      Class<?> repositoryInterface = metadata.getRepositoryInterface();
      JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());

      SimpleJpaRepository<?, ?> repo = isQueryDslExecutor(repositoryInterface) ? new QueryDslJpaRepository(
            entityInformation, entityManager) : new CustomSimpleJpaRepository(entityInformation, entityManager);
    repo.setLockMetadataProvider(lockModePostProcessor.getLockMetadataProvider());

      return repo;
  }
}

Pas encore fait, nous devons également avoir votre propre bean d'usine pour l'utiliser dans le xml de configuration:

public class CustomRepositoryFactoryBean <T extends JpaRepository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {

protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
    return new **CustomJpaRepositoryFactory**(entityManager);
}

}

la config:

<jpa:repositories base-package="bla.bla.dao" factory-class="xxxx.**CustomRepositoryFactoryBean**"/>

J'espère que cela aide.

8
Balamaci Serban

Si vous utilisez uniquement des interfaces, vous pouvez utiliser des méthodes par défaut pour effectuer des remplacements simples de CrudRepository ou JpaRepository:


public interface MyCustomRepository extends CrudRepository<T, ID> {

  @Override
  default <S extends T> S save(S entity)
  {
    throw new UnsupportedOperationException("writes not allowed");
  }
}
3
M.Paniccia

Cela pourrait être utile si vous envisagez de réutiliser la méthode d'origine. Il suffit d'injecter EntityManager dans la classe d'implémentation.

public interface MyEntityRepositoryCustom {
  <S extends MyEntity> S save(S entity);
}

public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {

    // optionally specify unitName, if there are more than one
    @PersistenceContext(unitName = PRIMARY_ENTITY_MANAGER_FACTORY)
    private EntityManager entityManager;

    /**
     * @see org.springframework.data.jpa.repository.support.SimpleJpaRepository
     */
    @Transactional
    public <S extends MyEntity> S save(S entity) {
        // do your logic here
        JpaEntityInformation<MyEntity, ?> entityInformation = JpaEntityInformationSupport.getMetadata(MyEntity.class, entityManager);
        if (entityInformation.isNew(entity)) {
            entityManager.persist(entity);
            return entity;
        } else {
            return entityManager.merge(entity);
        }
    }
}
2
ytterrr

Utilisez des écouteurs d'événements JPA comme @PrePersist, @PreUpdate. Cela fonctionnera si le fournisseur JPA sous-jacent prend en charge ces fonctionnalités. Il s'agit de la fonctionnalité JPA 2, donc la dernière version d'Hibernate, EclipseLink, etc. devrait la prendre en charge.

1
bhantol2

J'utilise Spring Boot 2.1.4 sur OpenJDK 11 et je reçois également l'erreur ambiguous reference Du compilateur (bien que le compilateur Eclipse JDT que mon IDE utilise n'a aucun problème avec , donc je n'ai pas découvert ce problème avant d'avoir essayé de le construire en dehors de mon IDE).

J'ai fini par définir une méthode avec un nom différent dans mon interface d'extension, puis j'ai utilisé une substitution default dans mon interface de référentiel principale pour l'appeler lorsque le save() normal était appelé.

Voici un exemple:

Définissez l'interface pour votre logique personnalisée comme d'habitude:

public interface MyEntityRepositoryCustomSaveAction {
    public MyEntity saveSafely(MyEntity entity);
}

Faites en sorte que votre référentiel étende cette interface:

public interface MyEntityRepository extends JpaRepository<MyEntity, MyEntityId>,
  MyEntityRepositoryCustomSaveAction {

    @Override
    @SuppressWarnings("unchecked")
    default MyEntity save(MyEntity entity)
    {
        return saveSafely(entity);
    }
}

Notez que nous avons remplacé save () de JpaRepository (enfin, vraiment CrudRepository qui JpaRepository s'étend) pour appeler notre méthode personnalisée. Le compilateur vous avertit de la conversion non contrôlée, donc à vous de décider si vous voulez la désactiver avec @SuppressWarnings.

Suivez la convention de la classe Impl avec votre logique personnalisée

public class MyEntityRepositoryCustomSaveActionImpl implements 
  MyEntityRepositoryCustomSaveAction {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public MyEntity saveSafely(MyEntity entity) {
       //whatever custom logic you need
    }

}
1
Erin Drummond

La solution de @ytterrr fonctionne mais pour les anciennes versions de Spring Data, pour Spring Data 2.1 au moins, c'est le moyen non seulement de remplacer n'importe quelle méthode de référentiel mais aussi d'accéder aux fonctionnalités sous-jacentes (accès à l'entité gestionnaire pour persister, supprimer, trouver ...):

public interface MyEntityRepositoryCustom {
  <S extends MyEntity> S save(S entity);
}

public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {

    final JpaEntityInformation<MyEntity, ?> entityInformation;
    EntityManager em;

    public MyEntityRepositoryImpl(EntityManager entityManager) {
        this.entityInformation = JpaEntityInformationSupport.getEntityInformation(MyEntity.class, entityManager);
        this.em = entityManager;
    }

    /**
     * @see org.springframework.data.jpa.repository.support.SimpleJpaRepository
     */
    @Transactional
    public <S extends MyEntity> S save(S entity) {

        // do your logic here

        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }
}
0
Mariano Ruiz

Afin de remplacer correctement la méthode de sauvegarde, vous devez créer une interface avec la signature appropriée de la méthode d'origine déclarée sur CrudRepository, y compris les génériques

public interface MyCustomRepository<T> {
    <S extends T> S save(S entity);
}

Ensuite, créez votre implémentation (le suffixe Impl est important au nom de la classe)

public class MyCustomRepositoryImpl implements MyCustomRepository<MyBean> {

    @Autowired
    private EntityManager entityManager;


    @Override
    public <S extends MyBean> S save(S entity) {
       /**
         your custom implementation comes here ...
         i think the default one is just        
        return this.entityManager.persist(entity);
       */
    }

}

Enfin, étendez votre référentiel avec l'interface précédemment créée

@RepositoryRestResource
@Repository
public interface MyBeanRepository extends PagingAndSortingRepository<MyBean, Long>, MyCustomRepository<MyBean> {}
0
Lucas