스프링/JPA

Spring Data JPA 구현체를 만들고 싶다면?

nomoreFt 2022. 2. 23. 00:08

 

💡
Spring Data JPA 레포지토리가 인터페이스만 정의하기 때문에 인터페이스 구현체를 만들 때 만들어야하는 기능이 너무 많다. 인터페이스의 메서드를 직접 구현하는 방법을 알아보자.

 

만약

Spring JPA data

를 상속하고 있는 Repository interface를 구현하려고 하려면 이 모든 method들을 구현해야 한다. 그러나 따로 메서드를 빼서 내가 원하는 구현을 하고 싶을 땐 어떻게 해야할까?

ex ) 직접 sql 접근 (JDBC Connection), QueryDsl 사용할 때 쿼리 직접 넣으니까 빼서 구현 등

 

  1. custom Interface 생성                                                                                                                  
    public interface MemberRepositoryCustom {     List<Member> findMemberCustom(); }
  1. custom Interface impl 생성
    @RequiredArgsConstructor 
    public class MemberRepositoryImpl implements MemberRepositoryCustom{
    
    	private final EntityManager em;     
        
        //직접 sql 접근(jdbc connection 맺거나 JPQL 하면 된다.     
        @Override     
        public List<Member> findMemberCustom() {         
        return em.createQuery("select m from Member m")                
        		 .getResultList();     
    	} 
    }
  1. 기존 SpringDataJPA Repository에 custom Interface 상속받는다.
    public interface MemberRepository extends JpaRepository<Member,Long>, MemberRepositoryCustom{
  1. 기존 SpringDataJPA Repository에서 구현된 method를 쓰면 구현체에서 실행된다.
    @Test     
    public void callCustom() throws Exception {        
        List<Member> memberCustom = memberRepository.findMemberCustom(); 				
        //CustomInterface에 정의됐고, Impl에서 구현한 구현체 		
    }

💥

유의사항 및 실무 팁
💡
Spring Data JPA ㅡ와 구현체의 명명 규칙 ex ) MemberRepository(SpringDataJPA 인터페이스) + Impl 붙여야 한다. (기본 Postfix 규칙) //MemberRepositoryImpl 그래야 Spring Data JPA가 인식해서 빈으로 등록한다. 구현체를 위해 JPA 인터페이스 상속용 Custom Interface는 아무렇게나 명명해도 된다.

 

핵심 Business 로직이 있는 repository랑

화면에 fit한 Repository를 최대한 분리해야 유지보수, 핵심기능 찾기가 편하다.

그냥 임의의 Repository를 Class로 구현해서 스프링 빈에 등록해서 쓰면 된다.

@Repository

 

//핵심 비즈니스 로직이 들어있는 기본 SpringDataJPA 인터페이스 Repository

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

	List<Member> findByusernameAndAgeGreaterThan(String username, int age);      
	@Query(name = "Member.findByUsername")     
	List<Member> findByUsername(@Param("username") String username); 
}  

//화면과 Fit한 메서드들을 모아 분리해놓은 Repository /Dto로 받아오는 것을 주로 뺀다. 

@Repository 
@RequiredArgsConstructor 
public class MemberQueryRepository {      
	private final EntityManager em;      
	public List<Member> findByusernameAndAgeGreaterThan(String username, int age){         
	return em.createQuery("select m from Member m where m.username = :username and m.age > :age")                 
			 .setParameter("username", username)                 
			 .setParameter("age", age)                 
			 .getResultList();     
	} 
}