Я рассматриваю данные spring для проекта. Можно ли переопределить метод сгенерированного по умолчанию? И если да, то как?
Spring Данные: метод отмены переопределения
Ответ 1
Не получилось, чтобы это работало хорошо, поэтому я поместил свою логику в класс обслуживания и оставил без сохранения резервные копии.
Ответ 2
Просто создайте свой пользовательский интерфейс, как обычно, и объявите там методы, которые вы хотите использовать, с той же подписью, что и с помощью CrudRepository
(или JpaRepository
и т.д.). Предположим, что у вас есть объект MyEntity
и репозиторий MyEntityRepository
, и вы хотите переопределить автоматически созданный по умолчанию метод save
MyEntityRepository
, который принимает единственный экземпляр объекта, а затем определите:
public interface MyEntityRepositoryCustom {
<S extends MyEntity> S save(S entity);
}
и реализуйте этот метод, как вам нравится в MyEntityRepositoryImpl
, как обычно:
@Transactional
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
public <S extends MyEntity> S save(S entity) {
// your implementation
}
}
И затем, как обычно, пусть MyEntityRepository
реализует MyEntityRepositoryCustom
.
Выполняя это, Spring Data JPA вызовет метод save
вашего MyEntityRepositoryImpl
вместо реализации по умолчанию.
По крайней мере, это работает для меня с помощью метода delete
в Spring Data JPA 1.7.2.
Ответ 3
Я предполагаю, что вы расширяете SimpleJpaRepository:
public class **CustomSimpleJpaRepository** extends SimpleJpaRepository {
@Transactional
public <S extends T> S save(S entity) { //do what you want instead }
}
Затем убедитесь, что это используется вместо стандартного SimpleJpaRepository, расширяя:
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;
}
}
Еще не сделано, нам также нужно иметь свой собственный factory bean, чтобы использовать его в config xml:
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);
}
}
config:
<jpa:repositories base-package="bla.bla.dao" factory-class="xxxx.**CustomRepositoryFactoryBean**"/>
Надеюсь, что это поможет.
Ответ 4
Чтобы обеспечить переопределение созданного по умолчанию метода сохранения, вам необходимо использовать агрегацию реализации репозитория Spring в своей собственной реализации репозитория.
Интерфейс репозитория:
public interface UserRepository extends CrudRepository<User, String>{
}
Реализация хранилища:
@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);
}
}
Затем используйте свою собственную реализацию в своей службе:
@Autowired
@Qualifier("customUserRepository")
private UserRepository userRepository;
Ответ 5
Это может быть полезно, если вы собираетесь повторно использовать оригинальный метод. Просто введите EntityManager
в класс реализации.
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);
}
}
}
Ответ 6
Если вы используете только интерфейсы, вы можете использовать методы по умолчанию для простых переопределений CrudRepository
или JpaRepository
:
public interface MyCustomRepository extends CrudRepository<T, ID> {
@Override
default <S extends T> S save(S entity)
{
throw new UnsupportedOperationException("writes not allowed");
}
}
Ответ 7
Используйте прослушиватели событий JPA, например @PrePersist, @PreUpdate. Это будет работать, если базовый поставщик JPA поддерживает эти функции. Это функция JPA 2, поэтому последняя версия Hibernate, EclipseLink и т.д. Должна поддерживать ее.
Ответ 8
Я использую Spring Boot 2.1.4 в OpenJDK 11, а также продолжаю получать ambiguous reference
ошибку ambiguous reference
от компилятора (хотя компилятор Eclipse JDT, который использует моя IDE, не имеет проблем с этим, поэтому я не обнаружил эту проблему, пока не попробовал построить его за пределами моей IDE).
В основном я определил метод с другим именем в моем интерфейсе расширения, а затем использовал переопределение по default
в моем основном интерфейсе репозитория, чтобы вызывать его при вызове обычного save()
.
Вот пример:
Определите интерфейс для вашей пользовательской логики как обычно:
public interface MyEntityRepositoryCustomSaveAction {
public MyEntity saveSafely(MyEntity entity);
}
Сделайте ваш репозиторий расширить этот интерфейс:
public interface MyEntityRepository extends JpaRepository<MyEntity, MyEntityId>,
MyEntityRepositoryCustomSaveAction {
@Override
@SuppressWarnings("unchecked")
default MyEntity save(MyEntity entity)
{
return saveSafely(entity);
}
}
Обратите внимание, что мы переопределили save() из JpaRepository
(ну, в действительности, это CrudRepository
который расширяет JpaRepository
) для вызова нашего пользовательского метода. Компилятор предупреждает о неконтролируемом преобразовании, так что до вас, если вы хотите заставить его замолчать с @SuppressWarnings
.
Следуйте соглашению для класса Impl с вашей собственной логикой
public class MyEntityRepositoryCustomSaveActionImpl implements
MyEntityRepositoryCustomSaveAction {
@PersistenceContext
private EntityManager entityManager;
@Override
public MyEntity saveSafely(MyEntity entity) {
//whatever custom logic you need
}
}
Ответ 9
Решение от @ytterrr работает, но для более старых версий Spring Data, по крайней мере для Spring Data 2.1, это способ не только переопределить любой метод репозитория, но и доступ к базовым функциям (доступ к диспетчеру сущностей для сохранения, удаления, поиска...):
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);
}
}
}
Ответ 10
Чтобы правильно переопределить метод save, вы должны создать интерфейс с правильной сигнатурой исходного метода, объявленного в CrudRepository, включая обобщенные значения.
public interface MyCustomRepository<T> {
<S extends T> S save(S entity);
}
Затем создайте свою реализацию (суффикс Impl важен в имени класса)
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);
*/
}
}
Наконец, расширьте свой репозиторий с помощью ранее созданного интерфейса
@RepositoryRestResource
@Repository
public interface MyBeanRepository extends PagingAndSortingRepository<MyBean, Long>, MyCustomRepository<MyBean> {}