Как добавить пользовательский метод в Spring Data JPA

Я изучаю Spring данные JPA. Рассмотрим приведенный ниже пример, где я получу все функции crud и finder, работающие по умолчанию, и если я хочу настроить finder, тогда это также можно легко сделать в самом интерфейсе.

@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {

  @Query("<JPQ statement here>")
  List<Account> findByCustomer(Customer customer);
}

Я хотел бы знать, как добавить полный пользовательский метод с его реализацией для вышеупомянутого AccountRepository? Поскольку его интерфейс не может реализовать метод там.

Ответ 1

Вам нужно создать отдельный интерфейс для своих пользовательских методов:

public interface AccountRepository 
    extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }

public interface AccountRepositoryCustom {
    public void customMethod();
}

и предоставьте класс реализации для этого интерфейса:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @Autowired
    @Lazy
    AccountRepository accountRepository;  /* Optional - if you need it */

    public void customMethod() { ... }
}

Смотрите также:

Ответ 2

В дополнение к axtavt answer, не забывайте, что вы можете внедрить Entity Manager в свою настраиваемую реализацию, если вам нужно, чтобы создавать ваши запросы:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    public void customMethod() { 
        ...
        em.createQuery(yourCriteria);
        ...
    }
}

Ответ 3

Это ограничено в использовании, но для простых пользовательских методов вы можете использовать методы интерфейса по умолчанию, такие как:

import demo.database.Customer;
import org.springframework.data.repository.CrudRepository;

public interface CustomerService extends CrudRepository<Customer, Long> {


    default void addSomeCustomers() {
        Customer[] customers = {
            new Customer("Józef", "Nowak", "[email protected]", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
            new Customer("Adrian", "Mularczyk", "[email protected]", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
            new Customer("Kazimierz", "Dejna", "[email protected]", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
            new Customer("Celina", "Dykiel", "[email protected]", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
        };

        for (Customer customer : customers) {
            save(customer);
        }
    }
}

РЕДАКТИРОВАТЬ:

В этом весеннем уроке написано:

Spring Data JPA также позволяет вам определять другие методы запросов, просто объявляя сигнатуру их методов.

Так что даже можно просто объявить метод как:

Customer findByHobby(Hobby personHobby);

и если объект Hobby является свойством Customer, Spring автоматически определит метод для вас.

Ответ 4

Принятый ответ работает, но имеет три проблемы:

  • Он использует недокументированную функцию Spring Data при именовании пользовательской реализации как AccountRepositoryImpl. В документации четко указано, что она должна называться AccountRepositoryCustomImpl, имя пользовательского интерфейса плюс Impl
  • Вы не можете использовать инжектор конструктора, только @Autowired, который считается плохой практикой
  • У вас есть круговая зависимость внутри пользовательской реализации (вот почему вы не можете использовать конструктор внедрения).

Я нашел способ сделать его идеальным, хотя не без использования другой недокументированной функции Spring Data:

public interface AccountRepository extends AccountRepositoryBasic,
                                           AccountRepositoryCustom 
{ 
}

public interface AccountRepositoryBasic extends JpaRepository<Account, Long>
{
    // standard Spring Data methods, like findByLogin
}

public interface AccountRepositoryCustom 
{
    public void customMethod();
}

public class AccountRepositoryCustomImpl implements AccountRepositoryCustom 
{
    private final AccountRepositoryBasic accountRepositoryBasic;

    // constructor-based injection
    public AccountRepositoryCustomImpl(
        AccountRepositoryBasic accountRepositoryBasic)
    {
        this.accountRepositoryBasic = accountRepositoryBasic;
    }

    public void customMethod() 
    {
        // we can call all basic Spring Data methods using
        // accountRepositoryBasic
    }
}

Ответ 5

Im, используя следующий код, чтобы получить доступ к сгенерированным методам поиска из моей пользовательской реализации. Получение реализации через bean factory предотвращает проблемы с созданием кругового bean.

public class MyRepositoryImpl implements MyRepositoryExtensions, BeanFactoryAware {

    private BrandRepository myRepository;

    public MyBean findOne(int first, int second) {
        return myRepository.findOne(new Id(first, second));
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        myRepository = beanFactory.getBean(MyRepository.class);
    }
}

Ответ 6

Учитывая ваш фрагмент кода, обратите внимание, что вы можете передавать только собственные объекты методу findBy ###, скажем, вы хотите загрузить список учетных записей, принадлежащих определенным клиентам, одно из них - это сделать

 @Query("Select a from Account a where a."#nameoffield"=?1")
      List<Account> findByCustomer(String "#nameoffield");

Сделать sue имя запрашиваемой таблицы является тем же, что и класс Entity. Для дальнейших реализаций, пожалуйста, взгляните на this

Ответ 7

Если вы хотите выполнять более сложные операции, вам может потребоваться доступ к Spring внутренним данным, и в этом случае следующее работает (как мое временное решение для DATAJPA-422):

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager entityManager;

    private JpaEntityInformation<Account, ?> entityInformation;

    @PostConstruct
    public void postConstruct() {
        this.entityInformation = JpaEntityInformationSupport.getMetadata(Account.class, entityManager);
    }

    @Override
    @Transactional
    public Account saveWithReferenceToOrganisation(Account entity, long referralId) {
        entity.setOrganisation(entityManager.getReference(Organisation.class, organisationId));
        return save(entity);
    }

    private Account save(Account entity) {
        // save in same way as SimpleJpaRepository
        if (entityInformation.isNew(entity)) {
            entityManager.persist(entity);
            return entity;
        } else {
            return entityManager.merge(entity);
        }
    }

}

Ответ 8

Здесь есть еще одна проблема. Некоторые люди ожидают, что добавление пользовательского метода в ваш репозиторий автоматически откроет их как службы REST в разделе "/search". К сожалению, это не так. Spring не поддерживает это в настоящее время.

Это функция "по дизайну", Spring data rest явно проверяет, является ли метод пользовательским методом и не показывает его как ссылку поиска REST:

private boolean isQueryMethodCandidate(Method method) {    
  return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);
}

Это вопрос Оливера Гирке:

Это по дизайну. Пользовательские методы репозитория не являются методами запросов, как они могут эффективно реализовать любое поведение. Таким образом, в настоящее время нам не удастся определить способ HTTP для раскрытия метода под. POST будет самым безопасным вариантом, но это не соответствует общие методы запросов (которые получают GET).

Подробнее см. эту проблему: https://jira.spring.io/browse/DATAREST-206

Ответ 9

Как указано в документированной функциональности, использование префикса Impl позволяет нам получить довольно чистое решение:

  • Определите в интерфейсе @Repository, скажем, MyEntityRepository, либо методы Spring Data, либо пользовательские методы.
  • Создайте класс MyEntityRepositoryImpl (Impl суффикс магия) в любом месте (даже не нужно находиться в том же пакете), который реализует только пользовательские методы и аннотирования такого класса с @Component ** (@Repository не будет работать).
    • Этот класс может даже внедрить MyEntityRepository через @Autowired для использования в пользовательских методах.


Пример:

Класс сущности:

package myapp.domain.myentity;

@Entity
public class MyEntity {

    @Id
    private Long id;

    @Column
    private String comment;

}

Интерфейс репозитория:

package myapp.domain.myentity;

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {

    // EXAMPLE SPRING DATA METHOD
    List<MyEntity> findByCommentEndsWith(String x);

    List<MyEntity> doSomeHql(Long id);

    List<MyEntity> useTheRepo(Long id);

}

Боб реализации пользовательских методов:

package myapp.infrastructure.myentity;

@Component // Must be @Component !!
public class MyEntityRepositoryImpl { // must have the repo name + Impl !!

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private MyEntityRepository myEntityRepository;

    @SuppressWarnings("unused")
    public List<MyEntity> doSomeHql(Long id) {
        String hql = "SELECT eFROM MyEntity e WHERE e.id = :id";
        TypedQuery<MyEntity> query = entityManager.createQuery(hql, MyEntity.class);
        query.setParameter("id", id);
        return query.getResultList();
    }

    @SuppressWarnings("unused")
    public List<MyEntity> useTheRepo(Long id) {
        List<MyEntity> es = doSomeHql(id);
        es.addAll(myEntityRepository.findByCommentEndsWith("DO"));
        es.add(myEntityRepository.findById(2L).get());
        return es;
    }

}

Небольшие недостатки, которые я выявил:

  • Пользовательские методы в Impl класса отмечены как неиспользуемые компилятором, поэтому @SuppressWarnings("unused") предложение.
  • У вас есть ограничение в один класс Impl. (Принимая во внимание, что в реализации обычных интерфейсов фрагментов документы предполагают, что у вас может быть много.)

Ответ 10

Я расширяю SimpleJpaRepository:

public class ExtendedRepositoryImpl<T extends EntityBean> extends SimpleJpaRepository<T, Long>
    implements ExtendedRepository<T> {

    private final JpaEntityInformation<T, ?> entityInformation;

    private final EntityManager em;

    public ExtendedRepositoryImpl(final JpaEntityInformation<T, ?> entityInformation,
                                                      final EntityManager entityManager) {
       super(entityInformation, entityManager);
       this.entityInformation = entityInformation;
       this.em = entityManager;
    }
}

и добавляет этот класс в @EnableJpaRepositoryries repositoryBaseClass.

Ответ 11

Примечание:

При создании пользовательских реализаций для репозиториев данных Spring:

Наиболее важной частью имени класса, соответствующей интерфейсу фрагмента, является постфикс Impl.