[Spring] JpaRepository ? @NoRepositoryBean? 사용 이유, 사용자 정의 레포지토리

hyewon jeong·2024년 5월 9일
1

Spring

목록 보기
59/65

🎈 1. JpaRepository?

  • Spring Data Jpa 는 Spring Data 에 존재하는 프로젝트 중 하나로 JpaRepository interface를 제공해 줍니다.
  • JpaRepository 는 Spring Common에서 제공해주는 PagingAndSortingRepository, CrudRepository, Repository interface를 상속 받고 있습니다.
  1. PagingAndSortingRepository : 페이징, 정렬을 지원하는 메서드 제공
  2. CrudRepository: CRUD 기능을 지원하는 메서드 제공
  3. Repository : Marker interface. 마커용 외에 다른 기능 없음 .

🎈 2. 왜 JpaRepository 는 annotation 없어도 Bean객체로 등록 되는 걸까?

public interface ExamRepository extends JpaRepository<Entity, Long>
  • 우린 특정 엔티티에 관한 레포지토리를 만들때 crud 기능 활용하기 위해 JpaRepository interface를 상속 받습니다.
    그래서 JpaRepository interface를 자세히 보았습니다.

📍 2-1. JpaRepository

@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>
  • JpaRepository interface를 보면 @NoReposotiryBean 라는 어노테이션을 볼 수 있습니다.
    그리고 상속받고 있는 인터페이스 또한 보두 살펴 보았습니다.

📍 2-2. ListCrudRepository, ListPagingAndSortingRepository, QueryByExampleExecutor

@NoRepositoryBean
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID>

@NoRepositoryBean
public interface ListPagingAndSortingRepository<T, ID> extends PagingAndSortingRepository<T, ID>

public interface QueryByExampleExecutor<T>
  • 상속 받고 있는 인터페이스에도 마찬가지로 @NoRepositoryBean을 달고 있습니다.

🎈 3. @NoRepositoryBean ?

  • 해당 인터페이스가 레포지토리의 용도로 사용되는 것이 아닌 단지 레포지토리에 관한 메서드 정의하는 인터페이스라는 것을 명시하고 해당 인터페이스를 실제 Bean객체로 등록되지 않게 하는 역할을 합니다.

그럼 도대체 JpaRepository 를 상속받은 인터페이스는 따로 구현체도 없고 , @Repository 어노테이션도 없어서 ComponentScan이 되는 것도 아닌데 어떻게 Spring Bean객체로 등록이 되는 걸까?

🎈 4. @EnableJpaRepositories ?

  • 그 이유는 @Configuration을 사용한 빈 설정 클래스에서 @EnableJpaRepositories 을 사용하기 때문입니다.

  • Jpa는 @EnableJpaRepositories annotation을 사용해서 JpaRepository를 활성화 해준다. 이 기능은 spring boot에서 자동으로 설정 되어 있다고 한다.

  • 해당 어노테이션을 따라가면 아래와 같이 정의되어 있는데 자세 희 보면 중간 에

  • JpaRepositoriesRegistrar.class 를 임포트하고 있는데 한번 더 따라가면

이는 JpaRepository들을 bean으로 등록하는 역할을 한다는 것을 알수 있다.

  • 또한 해당 어노테이션의 최상단에 설명에는 Spring Data repository를 위한 class들은 기본적으로 scan의 대상으로 설정한다고 합니다.

🎈 5-1. 사용 예

@NoRepositoryBean
public interface JpaDynamicRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
}
public class JpaDynamicRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements JpaDynamicRepository<T, ID> {
}
  • JpaRepository 인터페이스의 구현체인 SimpleJpaRepository 를 상속받아 해당 기능과 추가 기능을 만들 예정입니다.
@SpringBootApplication
@EnableAspectJAutoProxy
@EnableJpaRepositories(repositoryBaseClass = JpaDynamicRepositoryImpl.class)
public class examApplication extends SpringBootServletInitializer {

}

@EnableJpaRepositories(repositoryBaseClass = JpaDynamicRepositoryImpl.class) 으로 커스텀 레포지토리 구현체를 자동으로 빈으로 등록 할 수 있게하고 , JpaDynamicRepository 인터페이스가 빈으로 등록되지 않도록 @NoRepositoryBean 어노테이션을 달아 빈을 생성하지 못하도록 하는데 적용하였습니다.

🎈 5-2. 사용 예 ) 사용자레포지토리 다른 구조

@SpringBootApplication
public class QueryDsl3Application {

  public static void main(String[] args) {
    SpringApplication.run(QueryDsl3Application.class, args);
  }
}
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
 List<Member> findByUsername(String username);
}
public interface MemberRepositoryCustom {
  List<MemberTeamDto> search(MemberSearchCond condition);
  Page<MemberTeamDto> searchPageSimple(MemberSearchCond condition, Pageable pageable);
  Page<MemberTeamDto> searchPageComplex(MemberSearchCond condition, Pageable pageable);
}
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {

//-------사용자정의 레포지토리 --------- queryDsl -----------

  private final JPAQueryFactory jpaQueryFactory;

  public MemberRepositoryCustomImpl(EntityManager em) {
    this.jpaQueryFactory = new JPAQueryFactory(em);
  }

  //회원명, 팀명, 나이(ageGoe, ageLoe)
  @Override
  public List<MemberTeamDto> search(MemberSearchCond condition) {
    return jpaQueryFactory
        .select(Projections.constructor(MemberTeamDto.class,
            member.id, member.username, member.age, team.id, team.name))
        .from(member)
        .leftJoin(member.team, team)
        .where(usernameEq(condition.getUsername()),
            teamNameEq(condition.getTeamName()),
            ageGoe(condition.getAgeGoe()),
            ageLoe(condition.getAgeLoe()))
        .fetch();

  }

.......
}
profile
개발자꿈나무

0개의 댓글