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

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

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

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

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



답변

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

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

또한보십시오:


답변

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

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

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


답변

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

  • 커스텀 구현을로 명명 할 때 문서화되지 않은 스프링 데이터 기능을 사용합니다 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
    }
}


답변

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

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은 자동으로 메소드를 정의합니다.


답변

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


답변

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

답변

좀 더 정교한 작업을 수행하려면 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);
        }
    }

}