Spring Data JPA에 커스텀 메소드를 추가하는 방법


160

Spring Data JPA를 찾고 있습니다. 기본적으로 모든 크루 드 및 파인더 기능이 작동하는 아래 예제를 고려하고 파인더를 사용자 정의하려면 인터페이스 자체에서 쉽게 수행 할 수 있습니다.

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

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

위의 AccountRepository에 대한 구현으로 완전한 사용자 정의 메소드를 추가하는 방법을 알고 싶습니다. 인터페이스 때문에 메소드를 구현할 수 없습니다.

답변:


290

사용자 정의 메소드에 대해 별도의 인터페이스를 작성해야합니다.

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() { ... }
}

또한보십시오:


21
이 사용자 정의 구현이 실제 저장소를 주입 할 수 있습니까? 그래서 여기에 정의 된 메소드를 사용할 수 있습니까? 특히, 더 높은 레벨의 find 구현에서 Repository 인터페이스에 정의 된 다양한 find * 함수를 참조하고 싶습니다. find * () 함수에는 구현이 없으므로 Custom 인터페이스 또는 Impl 클래스에서 선언 할 수 없습니다.
JBCP

18
불행히도 Spring Data는 AccountRepository에 정의 된 모든 메소드에 대한 쿼리를 자동으로 생성하려고 시도하면서 "Account"객체에서 "customMethod"속성을 찾으려고합니다. 이것을 막을 방법이 있습니까?
Nick Foote 2016 년

41
@NickFoote는 저장소를 구현하는 클래스의 이름이 AccountRepositoryImplnot : AccountRepositoryCustomImpl등 이어야 함을 명심하십시오 . 이는 매우 엄격한 명명 규칙입니다.
제온

5
@ wired00 순환 참조를 생성한다고 생각하지만 @JBCP가 어떻게 작동하는지 볼 수 없습니다. 비슷한 것을 시도 할 때 예외가 발생합니다.Error creating bean with name 'accountRepositoryImpl': Bean with name 'accountRepositoryImpl' has been injected into other beans [accountRepository] in its raw version as part of a circular reference, but has eventually been wrapped.
Robert Hunt

6
예, 확장하는 경우 작동하지 않는 것에 대한 내 이전 의견을 참조하십시오. QueryDslRepositorySupport생성자 주입 대신 필드 또는 세터 주입을 통해 리포지토리를 주입해야합니다. 그렇지 않으면 Bean을 만들 수 없습니다. 그것은 효과가있는 것처럼 보이지만 솔루션은 약간 '더러운'것 같은 느낌이 듭니다 .Spring Data 팀의 작동 방식을 개선 할 계획이 있는지 확실하지 않습니다.
로버트 헌트

72

axtavt의 답변 외에도 쿼리를 작성하는 데 필요한 경우 사용자 정의 구현에 Entity Manager를 삽입 할 수 있음을 잊지 마십시오.

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

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

10
그러나 고맙지 만 사용자 지정 구현에서 Pageable 및 Page를 사용하는 방법을 알고 싶습니다. 어떤 입력?
완드 메이커

17

수락 된 답변은 효과가 있지만 세 가지 문제가 있습니다.

  • 커스텀 구현을로 명명 할 때 문서화되지 않은 스프링 데이터 기능을 사용합니다 AccountRepositoryImpl. 문서는 명확하게 호출 할 것을 명시 AccountRepositoryCustomImpl, 사용자 정의 인터페이스 이름과Impl
  • @Autowired나쁜 습관으로 간주되는 생성자 주입 만 사용할 수 없습니다.
  • 사용자 정의 구현 내부에 순환 종속성이 있습니다 (따라서 생성자 삽입을 사용할 수 없습니다).

문서화되지 않은 다른 스프링 데이터 기능을 사용하지 않고도 완벽하게 만드는 방법을 찾았습니다.

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
    }
}

이것은 효과가 있었다. 생성자에서 매개 변수 이름의 중요성을 강조하고 싶습니다.이 답변의 규칙을 따라야합니다 accountRepositoryBasic. 그렇지 않으면 스프링은 *Impl생성자 에 주입 할 두 가지 콩 선택에 대해 불평했습니다 .
염소

그래서 AccountRepository의 사용은 무엇인가
Kalpesh SONI

@KalpeshSoni 양쪽에서 방법 AccountRepositoryBasicAccountRepositoryCustom주입 된 통해 사용할 수AccountRepository
GEG

1
컨텍스트를 작성하는 방법을 알려주시겠습니까? 나는 그것을 모두 함께 넣을 수 없습니다. 감사합니다.
franta kocourek

12

사용에는 제한이 있지만 간단한 사용자 정의 메소드의 경우 다음 과 같은 기본 인터페이스 메소드를 사용할 수 있습니다 .

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", "nowakJ@o2.pl", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
            new Customer("Adrian", "Mularczyk", "adii333@wp.pl", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
            new Customer("Kazimierz", "Dejna", "sobieski22@weebly.com", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
            new Customer("Celina", "Dykiel", "celina.dykiel39@yahoo.org", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
        };

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

편집하다:

이번 봄 튜토리얼 에서는 다음과 같이 작성되었습니다.

Spring Data JPA를 사용하면 메소드 서명을 선언하여 다른 쿼리 메소드를 정의 할 수도 있습니다.

따라서 다음과 같은 메소드를 선언하는 것조차 가능합니다.

Customer findByHobby(Hobby personHobby);

객체 Hobby가 Customer의 속성이라면 Spring은 자동으로 메소드를 정의합니다.


6

내 사용자 정의 구현에서 생성 된 찾기 메소드에 액세스하기 위해 다음 코드를 사용하고 있습니다. 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);
    }
}

5

문서화 된 기능Impl명시된 바와 같이 접미사를 사용하면 다음과 같이 깨끗한 솔루션을 얻을 수 있습니다.

  • 에 정의 @Repository말하자면, 인터페이스 MyEntityRepository방법 또는 사용자 정의 방법을 어느 봄 데이터
  • 커스텀 메소드구현 하는 클래스 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);

}

사용자 정의 메소드 구현 Bean :

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클래스 로 제한 됩니다. (정규 조각 인터페이스 구현 에서 문서 는 많은 것을 가질 수 있다고 제안 합니다.)

테스트하는 동안 약간의 경고가 있습니다. 필요한 경우 알려 주시면 답변을 업데이트하겠습니다.
acdcjunior

MyEntityRepositoryImpl을 올바르게 자동 연결하는 방법은 무엇입니까?
Konstantin Zyubin

@KonstantinZyubin 당신은 autowire MyEntityRepository하지 않습니다 *Impl.
acdcjunior

4

좀 더 정교한 작업을 수행하려면 Spring Data의 내부에 액세스해야 할 수 있습니다.이 경우 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);
        }
    }

}

4

코드 스 니펫을 고려하면 기본 객체를 findBy ### 메소드에만 전달할 수 있으며 특정 costumers에 속한 계정 목록을로드하려고한다고 말할 수 있습니다. 하나의 해결책은이를 수행하는 것입니다.

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

쿼리 할 테이블의 이름이 Entity 클래스와 동일해야합니다. 추가 구현에 대해서는 이것을 살펴보십시오.


1
(가) 쿼리에 오타, 그것은 nameoffie해야 리터 D를, 나는 그것을 해결하기 위해 적절한 권한이 없습니다.
BrunoJCM

3

여기서 고려해야 할 또 다른 문제가 있습니다. 어떤 사람들은 사용자 정의 메소드를 저장소에 추가하면 자동으로 '/ search'링크 아래에서 REST 서비스로 표시 할 것으로 기대합니다. 불행히도 그렇지 않습니다. Spring은 현재이를 지원하지 않습니다.

이것은 '디자인에 의한'기능이며 스프링 데이터 레스트는 메소드가 사용자 정의 메소드인지 명시 적으로 확인하고 REST 검색 링크로 표시하지 않습니다.

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

올리버 지 에르케 (Oliver Gierke)

이것은 의도적으로 설계된 동작입니다. 사용자 정의 리포지토리 방법은 동작을 효과적으로 구현할 수 있으므로 쿼리 방법이 아닙니다. 따라서 현재 메소드를 공개 할 HTTP 메소드를 결정하는 것은 불가능합니다. POST는 가장 안전한 옵션이지만 GET을받는 일반적인 쿼리 방법과 일치하지 않습니다.

자세한 내용은이 문제를 참조하십시오 : https://jira.spring.io/browse/DATAREST-206


불행히도, 내가 뭘 잘못했는지 알아 내려고 너무 많은 시간을 낭비했으며, 마지막으로 그러한 기능이 없다는 것을 알고 있습니다. 그들은 왜 그 기능을 구현할까요? 콩을 줄이려면? 한 곳에서 모든 dao 방법을 사용하려면? 나는 다른 방법으로 그것을 달성 할 수있었습니다. "단일 리포지토리에 동작 추가"기능의 목표가 무엇인지 아는 사람이 있습니까?
Skeeve February

메소드에 @RestResource(path = "myQueryMethod")주석을 추가하여 REST를 통해 모든 저장소 메소드를 노출 할 수 있습니다 . 위의 인용문은 Spring이 어떻게 매핑을 원하는지 알지 못해서 (예 : GET vs POST 등) 주석을 통해 지정할 수 있음을 나타냅니다.
GreenGiant

1

모든 리포지토리에 사용자 지정 동작 추가 :

모든 리포지토리에 사용자 지정 동작을 추가하려면 먼저 중간 인터페이스를 추가하여 공유 동작을 선언하십시오.

public interface MyRepository <T, ID extends Serializable> extends JpaRepository<T, ID>
{

    void sharedCustomMethod( ID id );
}

이제 개별 리포지토리 인터페이스는 리포지토리 인터페이스 대신이 중간 인터페이스를 확장하여 선언 된 기능을 포함합니다.

다음으로, 지속성 기술 특정 저장소 기본 클래스를 확장하는 중간 인터페이스의 구현을 작성하십시오. 그런 다음이 클래스는 저장소 프록시의 사용자 정의 기본 클래스로 작동합니다.

public class MyRepositoryImpl <T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID>
{

    private EntityManager entityManager;

       // There are two constructors to choose from, either can be used.
    public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager)
    {
        super( domainClass, entityManager );

        // This is the recommended method for accessing inherited class dependencies.
        this.entityManager = entityManager;
    }


    public void sharedCustomMethod( ID id )
    {
        // implementation goes here
    }
}

스프링 데이터 리포지토리 Part I. Reference 여기에 이미지 설명을 입력하십시오


0

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에 추가합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.