스프링데이터 + JPA/스프링 데이터 JPA

20. 확장기능. 사용자 정의 리포지토리

sdafdq 2023. 11. 23. 12:29

이거는 정말 많이 쓸 것이다.

 

이게 뭐냐면, 우리가 기존의 SpringDataJPA에서는 메소드명으로 쿼리 지정, 뭐 JPQL직접 작성 등 여튼간에 SpringDataJPA에서가 제공해주는 방식으로만 정의할 수 있었다. 인터페이스이기 때문에. SpringDataJPA이란 JPA를 이용한 것들의 자동구현을 도와주는 라이브러리 이기 때문에.

 

근데 만약 우리가 jpql이 아니라 순수 sql을 이용해, jdbc template를 이용해 DB에 쿼리를 전달하고 싶다면?

아니면 QueryDSL을 쓰고 싶다면?

어딘가에 직접 우리가 그걸 구현해 줘야 한다.

 

아쉽게도, SpringDataJPA가 직접 QueryDSL과 뭐 연동해서 해 주는 것은 없다.

 

우리가 직접 구현을 해 줘야 한다. 

그러면 어떻게 하느냐,

 

먼저, 따로 인터페이스가 필요하다.

public interface MemberRepositoryCustom {
    List<Member> findMemberCustom();
}

 

저 인터페이스의 이름은 아무렇게나 해도 된다.

여튼간에 저렇게 메소드를 정의시켜 주고,

 

@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {
    private final EntityManager em;
    @Override
    public List<Member> findMemberCustom() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
}

구현해 주면된다.

이것의 추상체, 인터페이스의 이름은 상관없는데,

구현체의 이름은 상관이 있다.

 

SpringDataJPA 인터페이스의 이름 + Impl이 붙어야 한다.

우리가 SpringDataJPA를 MemberRepository라고 지었으니,

MemberRepositoryImpl이 되어야 한다.

 

그 후,

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
.....

저기 위해 잘 봐라.

저 인터페이스의 이름을 상속받았다.

저렇게 해 놓으면,

알아서 구현체를 찾아서, 즉 자신(SpringDataJpa의 인터페이스)의 이름 + Impl을 찾아서 알아서 가져와 호출한다.

 

select
    m1_0.member_id,
    m1_0.age,
    m1_0.team_id,
    m1_0.username 
from
    member m1_0

실제로 쿼리가 제대로 나간다.

 

QueryDSL이나 Jdbc template로 직접 DB에 sql 쿼리를 날리고 싶을 때 유용하겠다.

 

커스텀용 인터페이스를 아무 이름으로 짓는다. 거기에 구현 할 메소드를 선언한다.

그 커스텀용 인터페이스를 SpringDataJpa의 인터페이스명 + Impl로 이름을 지어 구현체, 즉 클래스를 만든다.

거기서 커스텀용 인터페이스를 implements 하여 그 선언한 메소드를 구현한다.

 

그 다음, 그 SpringDataJpa에 그것의 인터페이스를 같이 상속받는다.

얘는 인터페이스니 인터페이스만 상속받을 수 있다.

근데 구현체 이름은 자기이름(SpringDataJpa의 인터페이스) + Impl이니 알아서 잘 찾는다.

 

public interface 커스텀할거야 {
    List<Member> findMemberCustom();
}

선언

@RequiredArgsConstructor
public class MemberRepositoryImpl implements 커스텀할거야 {
    private final EntityManager em;
    @Override
    public List<Member> findMemberCustom() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
}

구현

public interface MemberRepository extends JpaRepository<Member, Long>, 커스텀할거야 {
.....

그 인터페이스를 상속

 

이게 실제로 된다. 인터페이스 이름은 뭘로 지어도 상관이 없는거니.

 

MemberRepositoryImple, 

즉 SpringDataJpa 인터페이스 + Impl이 중요하다.

그리고 상속받는 것은 SpringDataJpa가 인터페이스니 구현한 것의 추상체인 인터페이스를 상속받는 것.

이름은 관례에 맞춰서 저렇게 하면 알아서 찾는다.

 

SpringDataJpa가 저것의 구현체를 만들 때, 뭔가 JpaRepository말고의 인터페이스를 상속받네? 하고 자기 이름 + Impl을 찾아서 그걸 가져와서 상속 받는 듯 하다.

 

여튼 이건 굉장히 유용하겠다.

 

 

 이 impl 대신 다른 이름도 쓰도록 설정할 수 있다.

그런데 그냥 관례를 따르자.

 

QueryDSL이나 JDBC 템플릿으로 따로 구현하고 싶을 때 사용

 

 

 

혹시나 해서 말하는 건데,

핵심 비즈니스 로직과 화면에 맞춘 로직은 분리하는 것이 좋다.

 

그러니까, 아예 리포지토리 2개를 써라.

 

뭐 MemberQueryRepository 이렇게 따로 em등이나 queryDSL이용해서 구현체를 만들어라.

SpringDataJpa에서 기본으로 제공하는 DB 소통 메소드가 아니라 뭐 보통 Dto니 이런 것은 QueryDSL이나 따로 jpql을 작성해야 하니까 Query라고 쓴 듯

public interface MemberRepository extends JpaRepository<Member, Long>, MemberCustomRepository {

......
@Repository
@RequiredArgsConstructor
public class MemberQueryRepository {
    private final EntityManager em;

   	.....
}

 

뭐 이런 식으로 이렇게 2개 만드는..

 

MemberCustomRepository는 핵심 비즈니스 로직중에 필요한 것을 QueryDSL이나 JdbcTemplate로 직접 구현해야 할 때.

 

아래껀 화면에 맞춘 것들. Dto라던지 등

 

 

'스프링데이터 + JPA > 스프링 데이터 JPA' 카테고리의 다른 글

22. Web확장 도메인 클래스 컨버터  (0) 2023.11.24
21. Auditing  (0) 2023.11.24
19. JPA 힌트, Lock  (0) 2023.11.23
18. @EntityGraph  (0) 2023.11.23
17. 벌크성 수정쿼리  (0) 2023.11.23