Подтвердить что ты не робот

Spring Данные: метод отмены переопределения

Я рассматриваю данные spring для проекта. Можно ли переопределить метод сгенерированного по умолчанию? И если да, то как?

4b9b3361

Ответ 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> {}