Develop/spring-data

[Querydsl] Querydsl 사용방법 3가지

에디개발자 2020. 12. 7. 07:00
반응형

직장 동료의 조언 덕분에 Querydsl 설정 방법에도 여러가지가 있다는 것을 알았습니다. 

지난번에 작성한 Spring Data JPA 기반 Querydsl 사용해보자. ( Entity 관계 매핑 편, 테스트 코드 포함 )글에서는 QuerydslRepositorySupport를 상속받아 사용하는 방법에 대해서 작성하였습니다. 

이번글에서는 이 것외에도 2가지가 더 있고 어떤 것을 선택했는지까지 작성하겠습니다.

모든 소스는 Github에 있습니다. 

이동욱님의 티스토리를 참조해서 작성하였습니다. (https://jojoldu.tistory.com/372)

나를 닮았다고 한다...

Querydsl 사용방법 3가지

  1. QuerydslRepositorySupport 상속받아 사용
  2. JpaRepistory에서 Querldsl 사용 가능하도록 설정하여 사용
  3. 상속 구현없이 Querydsl만 사용

1. QuerydslRepositorySupport 상속받아 사용

현재 진행중인 프로젝트에서 기존에 썼던 방식입니다.

@Repository
public class StoreRepositorySupport extends QuerydslRepositorySupport {
    private final JPAQueryFactory jpaQueryFactory;

    /**
     * Creates a new {@link QuerydslRepositorySupport} instance for the given domain type.
     *
     * @param domainClass must not be {@literal null}.
     */
    public StoreRepositorySupport(JPAQueryFactory jpaQueryFactory) {
        super(Store.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }

    public Store findOneByName(String name) {
        return jpaQueryFactory
                .selectFrom(store)
                .where(store.name.eq(name))
                .fetchOne();
    }

    public List<StaffVo> findStaffsByName(String name) {
        return jpaQueryFactory
                .select(Projections.fields(StaffVo.class,
                        staff.id
                        , staff.age
                        , staff.name
                ))
                .from(store)
                .join(store.staff, staff)		// 1)
                .where(store.name.eq(name))
                .fetch();
    }
}

소스에서 QuerydslRepositorySupport 를 상속받아 사용할 수 있습니다. 생성자에서 super(domain.class); 를 설정하고 JPAQueryFactory를 사용합니다.

 

public interface StoreRepository extends JpaRepository<Store, Long> {
    Store findByName(String name);
}
jpa로 작성가능한 간단한 쿼리는 StoreRepository를 사용
inner join 등 복잡성이 높은 쿼리는 StoreRepositorySupper 를 사용

Service에서 의존성을 2개 선언해야합니다.

장점이라고 생각하면 명확하게 2개로 분리되있기 때문에 좋다!

단점이라고 생각하면 Service에 의존성이 불필요하게 두개씩 생긴다!

 

2. JpaRepistory에서 Querldsl 사용 가능하도록 설정하여 사용

이 방법으로 최종 결정되어 적용하였습니다.

총 3가지 파일을 생성합니다. 

StaffRepository

JpaRepository, CustomizedStaffRepository을 상속받습니다.

Service에서 의존성 주입하여 사용할 interface입니다.
public interface StaffRepository extends JpaRepository<Staff, Long>, CustomizedStaffRepository {
}

 

CustomizedStaffRepository

StaffRepository에 상속시킬 interface입니다.

StaffRepository에서 CustomizedStaffRepositoryImpl에서 작성된 로직을 실행할 수 있게 하는 interface입니다.

public interface CustomizedStaffRepository {
    List<Staff> searchAll();
    StaffVo search(Long id);
    Staff findStaffById(Long id);
}

 

CustomizedStaffRepositoryImpl 

CustomizedStaffRepository를 implements하여 Querydsl을 구현할 class입니다.

import static com.example.querydsl.staff.entity.QStaff.staff;

@RequiredArgsConstructor    // 의존성 주입
public class CustomizedStaffRepositoryImpl implements CustomizedStaffRepository {
    private final JPAQueryFactory jpaQueryFactory;


    @Override
    public List<Staff> searchAll() {
        return jpaQueryFactory
                .selectFrom(staff)
                .fetch();
    }

    @Override
    public StaffVo search(Long id) {
        return jpaQueryFactory
                .select(Projections.fields(StaffVo.class,
                    staff.id,
                    staff.name
                ))
                .from(staff)
                .where(staff.id.eq(id))
                .fetchOne();
    }

    @Override
    public Staff findStaffById(Long id) {
        return jpaQueryFactory
                .selectFrom(staff)
                .where(staff.id.eq(id))
                .fetchOne();
    }
}

 

Test 코드를 작성해서 테스트하면 모두 성공으로 결과값이 나옵니다.

@ActiveProfiles("local")
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class StaffRepositoryTest {

    @Autowired
    private StaffRepository staffRepository;

    @Test
    void searchAll() {
        //given


        //when
        List<Staff> staff = staffRepository.searchAll();

        //then
        assertThat(staff).isNotNull();
    }

    @Test
    void search() {
        //given
        final Long id = 1L;

        //when
        StaffVo search = staffRepository.search(id);

        //then
        assertThat(search.getId()).isEqualTo(id);
    }

    @Test
    @Transactional
    @Rollback(value = false)
    void find_후_저장테스트() {  // querydsl로 Entity를 조회하고 값을 변경하고 transaction이 끝나면 변경 컬럼이 Update된다.
        //given
        final Long id = 1L;
        final String name = "임임용태";
        Staff staff = staffRepository.findStaffById(id);

        //when
        staff.changeName(name);

        //then
    }
}

Service에서 의존성을 1개만 주입해서 JPA, Querydsl 모두 쓸 수 있습니다.

Service 의존성 리스트가 훨씬 줄어들겠네요!

 

3. 상속, 구현없이 Querydsl만 사용

조졸도님이 선호하는 방법이라고 합니다. :) ( 티스토리 참조 ) 

이 방법은 테스트해보지 않았습니다. JpaRepository에서 쉽게 호출가능한 쿼리들도 모두 구현해야할 것 같아서 제외하고 생각하였습니다.

 

구현 방법은 글 초입부에 링크를 걸어두었습니다. 참조해주세요 :)

 

 

결론

어떤 방법이 좋다! 는 없습니다. 

개발은 개인, 팀의 취향이라고 생각합니다. 개인이라면 취향에 맞게 사용하시면 되고, 팀이라면 팀원들과 충분한 커뮤니케이션 후 결정하시면 될 것 같습니다 :) 

 

 

p.s Spring에서 아직 Querydsl을 적극적으로 지원해준다는 생각은 들지 않네요.. ㅠ

반응형