JPA 로드맵 마지막 강의 실전 Querydsl을 수강하고 기억할 내용들과 후기를 남겨봅니다.
[목차]
[관련 포스팅]
1. 강의 느낀점
스프링 로드맵의 마지막 강의 Querydsl을 드디어 완강했습니다. 사실 JPA를 좀 더 쉽게 써주는 라이브러리 정도를 공부하는데에 시간대비 괜찮은 강의일까? 라는 의문도 조금 있었습니다만, 역시 김영한님의 강의력과 팁들 덕분에 시간과 돈이 오히려 절약되었다는 생각을 많이 했습니다.
그리고 Querydsl강의지만 Querydsl의 문법만 배우는 것이 아니고 지금까지 로드맵을 수강하며 쌓인 JPA와 스프링 지식 등을 동반해야 Querydsl을 이해할 수 있기 때문에 충분히 많은 것을 배운 강의었다는 생각이 듭니다.
사실 이전 로드맵 Spring Data JPA를 수강하지 못하고 왔으나, Querydsl을 이해하는데에 큰 어려움은 없던 것 같습니다. Spring Data JPA도 JPA를 쉽게 써주는 프레임워크 중 하나이기 때문에 JPA 로드맵 기초강의를 잘 들었다면 원활하게 강의를 들을 수 있을 것이라고 생각했습니다. Querydsl 강의 중간에 Spring Data JPA에 대한 내용도 조금 들어가기 때문에 큰 무리는 없었습니다.
2. 강의에서 기억할 것
이번 강의는 Querydsl의 문법이 주된 내용이지만 추후 실무 사용시 찾아보는 게 더 좋을 것 같고(일부 예제 및 학습 내용은 약간의 코드 작성) 여기에서는 알아야할 내용이나 실무 팁, 꼭 기억해야할 내용 등을 정리해봤습니다.
아래 정리 내용은 Member, Team 다대일 양방향 관계 설정을 기반으로 학습한 내용입니다.
- Querydsl은 복잡한 JPQL 작성법을 손쉽게 할 수 있게 도와줌
- 페이징 처리 기능과 동적쿼리가 가능하기 때문에 실무에서 Querydsl은 거의 필수적
- 설정하는게 처음엔 조금 복잡해보임. gradle 기준으로 querydsl-apt(Q파일 만드는 것과 관련)와 querydsl-jpa(실제 쿼리 만드는 것과 관련) 의존성을 주입해주고 약간의 설정이 더 들어감.
- build시 build폴더에 Q파일이 생성되며 이것을 갖고 querydsl을 짤 수 있음(static import 하여 사용하면 편함)
- QMember, QUser, QOrder 등..
- Querydsl의 또 다른 장점은 위에 Q 객체를 자바 문법을 통해 사용하기 때문에 컴파일 시점에 문법 오류를 잡아줌
- EntityManager(이하 em) 이외에 JPAQueryFactory(이하 jqf)를 빈 주입 받아 사용해야함. jqf는 em에 의존함. em은 동시성 문제가 없기 때문에 jqf 역시 동시성 문제가 없음(클래스 변수로 선언해서 사용해도 됨)
- 아래 코드 처럼 동적쿼리 사용법이 다양함
//동적쿼리 and() 사용하기
@Test
public void search() {
Member findMember = queryFactory.selectFrom(member)
.where(member.username.eq("member1")
.and(member.age.eq(10)))
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
//and() 대신 쉼표(,) 사용하기
@Test
public void searchAndParam() {
Member findMember = queryFactory.selectFrom(member)
.where(
member.username.eq("member1"),
(member.age.eq(10)))
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
- 집합함수, groupBy() 등 있어야 할 것들은 다 있음
- 여러가지 필드를 선택하여 select 받을 때 Tuple 타입으로 받게 됨
- leftJoin사용시에 정상 작동시키려면 on절에서 필터링을 해야한다. leftJoin에서 where절은 leftJoin의 의미가 없음(아래 코드 참고)
/**
* ex)회원과 팀을 조인하면서, teamA인 대상만 조인, 회원은 모두 조회
* JPQL: select m, t from member m left join m.team t on t.name = 'teamA'
* leftJoin사용시에 정상작동시키려면 on절에서 필터링을 해야한다. leftJoin에서 where절은 leftJoin의 의미가 없다.
*/
@Test
public void join_on_filtering() {
List<Tuple> result = queryFactory //select결과티입이 여러개라 tuple로 조회
.select(member, team)
.from(member)
.leftJoin(member.team, team)
.on(team.name.eq("teamA"))
.fetch();
for (Tuple tuple : result) {
System.out.println("tuple = " + tuple);
}
}
- 페치조인은 join뒤에 fetchJoin() 붙여주면 됨(아래 코드 참고)
//페치조인 사용시에는 join에 fetchJoin()만 붙이면 된다
@Test
public void fetchJoinUse() {
em.flush();
em.clear();
Member findMember = queryFactory
.selectFrom(member)
.join(member.team, team).fetchJoin()
.where(member.username.eq("member1"))
.fetchOne();
//LAZY로딩 대상인 team이 실제로 로드 되었는지 확인 할 수 있다.
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("페치 조인 미적용").isTrue();
}
- 서브쿼리는 JPAExpression을 쓰면 됨
- from절에 서브쿼리 불가(하이버네이트6도 불가)
- select 조건 여러개 가져올 때 리포지토리에서 Tuple로 받은 후 DTO 전환 권장
- 바로 DTO로 받기(아래 코드 참고)
//UserDto에 이름이 안 맞을 때는 as()를 쓰고 서브쿼리는 Expressions.as() 사용
@Test
public void findUserDto() {
QMember memberSub = new QMember("memberSub");
List<UserDto> result = queryFactory
.select(Projections.fields(UserDto.class,
member.username.as("name"),
Expressions.as(JPAExpressions
.select(memberSub.age.max())
.from(memberSub), "age")
))
.from(member)
.fetch();
for (UserDto userDto : result) {
System.out.println("userDto = " + userDto);
}
}
- 동적쿼리는 BooleanBuilder 방식과 Where 방식 있음
- Where에 직접 선언하는 방식이 더 깔끔
- 수정, 삭제 벌크 연산
- update하면 영속성 컨텍스트(1차 캐시) 무시 후 바로 DB 커밋됨
- 이상태에서 조회하면 1차 캐시와 DB조회 값 불일치
- 조회시 1차캐시가 우선되기에 DB값 버려짐
- 그래서 update 후 em.flush(), em.clear()로 영속성컨텍스트 DB갱신 및 초기화를 해주는 것이 좋음
- 뽀나스..스프링 데이터 JPA 클래스 선언하려면 아래 코드 참고
- 스프링 데이터 JPA는 find(), findAll()등 기본적인 CRUD 있음
- 아래처럼 findByUsername() 등 룰을 지켜 쓰면 JPQL로 알아서 해석해줌
public interface MemberRepository extends JpaRepository<Member, Long> {
//select m from Member m where m.username = ?
List<Member> findByUsername(String username);
}
- Querydsl에서 인자로 Pageable 넘겨서 사용하면 손쉽게 페이징 가능
- CountQuery 최적화할 수도 있음. 굳이 Count쿼리 날릴 필요 없을 때(페이징 필요 없을 때) 안 날림
- 페이지 크기가 컨텐츠 사이즈보다 클 때 굳이 페이징 계산을 위해 count날릴 필요 없음
- 마지막 페이지 일 때 (offset + 컨텐츠 사이즈를 더해서 전체 사이즈 구함, 더 정확히는 마지막 페이지이면 서 컨텐츠 사이즈가 페이지 사이즈보다 작을 때
3. 마치며
개인 정리용으로 쓴 것이라 스킵한 부분도 있어서 가독성이 좋지는 않을 것 같습니다.
문법 정리할게 많은데 굳이 포스팅으로 다 정리해도 나중에 결국 까먹고 안 보게 되더라구요.
그래서 기억할 부분과 코드 몇가지 예시만 넣어서 정리해봤습니다.
지금까지 배운 내용들을 기반으로 회사 실무에 적용할 수 있으면 좋겠는데 가능할지 모르겠습니다.
그게 안 되면 간단한 토이프로젝트라도 해서 체득을 좀 더 해야겠다고 느낍니다.
어쨌든 좋은 강의를 거의 두 달 정도의 기간동안 꾸준히 학습하면서 JPA를 어느 정도 이해할 수 있게 되었습니다.
요즘같이 강의 듣기 좋은 환경에 감사함을 느낍니다.
감사합니다!
'개발자 일지 > JPA' 카테고리의 다른 글
[강의 후기] 인프런 김영한님 스프링부트와 JPA 활용2 강의 후기 (0) | 2024.11.06 |
---|---|
[강의 후기] 인프런 김영한님 스프링부트와 JPA 활용1 강의 후기 (1) | 2024.10.08 |
[JPA 기초] Entity와 EntityManager 개념, 관계 (0) | 2024.09.21 |
[인프런 JPA]자바 ORM 표준 JPA 프로그래밍 정리2 (0) | 2023.01.31 |
[인프런 JPA]자바 ORM 표준 JPA 프로그래밍 정리1 (0) | 2023.01.28 |