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?
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.
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.
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;
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.
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");
}
}
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);
}
}
}
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.
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
}
}
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);
}
}
}
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> {}