인터페이스의 상속
- 인터페이스의 조상은 인터페이스만 가능
- 다중 상속이 가능
인터페이스는 위와 같은 속성을 가지고 있다는 것을 알아야한다.
사용자 정의 리포지토리를 구현하는 이유는
JPA 직접 사용( EntityManager )
스프링 JDBC Template 사용
MyBatis 사용 데이터베이스 커넥션 직접 사용 등등...
Querydsl 사용
등을 하기 위해서이다.
JPA repository는 인터페이스다. 즉 인터페이스가 제공하는 기능만을 사용할 수 있다.
하지만 JPA repository에 기능을 추가하려면? 기능을 추가한 JPA repository를 다 만들어야한다. (인터페이스이기 때문에)
따라서 JPA repository를 다 만든다는 것은 너무나 방대한 일이기때문에 불가능하다.
따라서 JPA repository와 사용자 정의 repository를 다중 상속하여
MemberRepository에서 JPA repository와 사용자 정의 repository 모두 사용할 수 있게 하는 것이다.
사용자 정의 리포지토리를 구현하려면 JpaRepository를 사용하는 인터페이스에 다중 상속을 하면 된다.(QueryDSL도 같은 방법으로 구현한다.)
주의 해야할것은
MemberRepository의 사용자정의 repository의 이름을 MemberRepositoryImpl로 해야한다는 것이다. 왜냐하면 JPA에서 구현체를 가져다 사용해야하는데 그 규칙이 인터페이스이름+Impl을 가져다 쓰는 것이기 때문이다.
MemberRepositoryImpl
✔ 하지만 JpaRepository로 구현 안되는 기능이라고 전부 MemberRepositoryCustom에 때려박고
MemberRepository를 통해 사용하는것은 옳은 방법이 아니다. 왜냐하면 코드가 커지면 커질 수록
MemberRepository의 크기도 커질 것이고 그렇게 되면 너무 코드가 지저분해진다.
코드가 지저분해진다는 것은 핵심코드가 뭔지를 알 수 가 없다. 따라서 아예 MemberQueryRepository같은 이름으로 클래스를 분리하여 사용하는 것이 더 나은 방법 일 수 있다.
//==JPA repository==//
public interface MemberRepository extends JpaRepository<Member, Long>,MemberRepositoryCustom {
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
List<Member> findHelloBy();
@Query(name = "Member.findByUsername")
List<Member> findByUsername(@Param("username") String username);
@Query("select m from Member m where m.username = :username and m.age = :age")
List<Member> findUser(@Param("username") String username, @Param("age") int age);
@Query("select m.username from Member m")
List<String> findByUsernameList();
@Query("select new stduy.datajpa.dto.MemberDto(m.id,m.username, t.name) from Member m join m .team t")
List<MemberDto> findMemberDto();
@Query("select m from Member m where m.username in :names")
// List<Member> findByUsername(@Param("names") List<String> names);
List<Member> findByUsername(@Param("names") Collection<String> names);
List<Member> findListByUsername(String username); //컬랙션
Member findMemberByUsername(String username); //단건
// List<Member> findMemberByUsername(String username); //단건
Optional<Member> findOptionalByUsername(String username);//단건 Optional
Page<Member> findByAge(int age, Pageable pageable);
// @Query(value = "select m from Member m where m.age =:age ORDER BY m.username desc",
// countQuery = "select count(m.username) from Member m")
// Page<Member> findByAge(@Param("age") int age,Pageable pageable);
List<Member> findTop3ByAge(int age);
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
// @EntityGraph(attributePaths = ("team"))
// List<Member> findEntityGraphByUsername(@Param("username") String username);
// @EntityGraph(attributePaths = ("team"))
// List<Member> findEntityGraphByUsername(String username);
@EntityGraph("Member.all")
List<Member> findEntityGraphByUsername(String username);
@QueryHints(value = @QueryHint( name = "org.hibernate.readOnly",value = "true"))
Member findReadOnlyByUsername(String username);
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findLockByUsername(String username);
}
//==사용자 정의 인터페이스==//
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")
.getResultList();
}
}