JPQL 페이징, 조인과 조인 ON 절
JPQL 페이징 방법과 다양한 조인들에 대해서 알아본다
페이징
IntStream.rangeClosed(1, 50).forEach(i
-> em.persist(new Member("member" + 1, i)));
List<Member> result = em.createQuery
("select m from Member m order by m.age desc", Member.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
- order by로 정렬이 가능하다. 나이를 내림차순으로 지정하였다.
- setFirstResult로 조회 시작 위치를 지정할 수 있다. 0부터 시작이므로 11번째부터 가져올 것이다.
- setMaxResults로 데이터 수를 지정할 수 있다. 10을 지정하였으므로 10개를 가져올 것이다.
Hibernate:
select
... 생략
from
Member member0_
order by
member0_.age desc limit ? offset ?
Member(id=40, name=member1, age=40)
Member(id=39, name=member1, age=39)
Member(id=38, name=member1, age=38)
Member(id=37, name=member1, age=37)
Member(id=36, name=member1, age=36)
Member(id=35, name=member1, age=35)
Member(id=34, name=member1, age=34)
Member(id=33, name=member1, age=33)
Member(id=32, name=member1, age=32)
Member(id=31, name=member1, age=31)
- 출력을 확인해보면 나이를 내림차순으로 조회하고 조회 시작 위치와 데이터 수가 정상적으로 지정된 것을 확인할 수 있다.
- 현재는 DB를 H2로 설정하였으므로 H2에 맞는 적절한 SQL로 조회를 한다.
방언 변경
- 현재는 H2에 맞는 SQL을 날려주지만 dialect 설정을 통해 간단하게 다른 DBMS의 SQL로 변경이 가능하다.
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
>>>> 아래로 변경 <<<<
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle8iDialect"/>
Hibernate:
select
*
from
( select
row_.*,
rownum rownum_
from
( select
생력...
from
Member member0_
order by
member0_.age desc ) row_ )
where
rownum_ <= ?
and rownum_ > ?
- hibernate.dialect을 H2에서 Oracle로 변경 후 실행을 해보면 JPA에서 알아서 Oracle에 맞는 SQL을 날려준다.
조인
- SQL과 문법이 거의 비슷하다. 하지만 JPQL은 엔티티로 조인을 한다
Inner, Outer Join
List<Member> innerJoin = em.createQuery
("select m from Member m inner join m.team t", Member.class)
.getResultList();
List<Member> leftOuterJoin = em.createQuery
("select m from Member m left join m.team t", Member.class)
.getResultList();
Hibernate:
select
생략 ..
from
Member member0_
inner join
Team team1_
on member0_.team_id=team1_.team_id
Hibernate:
select
생략 ..
from
Member member0_
left outer join
Team team1_
on member0_.team_id=team1_.team_id
- SQL을 확인해보면 정상적으로 조인이 동작한다.
Theta Join
- 세타 조인을 통해 연관관계가 없는 엔티티를 조회할 수 있다.
- 세타 조인을 하게 되면 각 행과 상대방 테이블의 행을 모두 조인하는 Cartesian product를 수행하게 된다.
em.persist(new Member("A", 10));
em.persist(new Member("B", 10));
em.persist(new Member("C", 10));
em.persist(new User("A"));
em.persist(new User("B"));
List<Member> resultList = em.createQuery
("select m from Member m, User u where m.name = u.name", Member.class)
.getResultList();
resultList.forEach(System.out::println);
- Member name과 User name이 같은 Member들을 조회한다.
Hibernate:
select
생략...
from
Member member0_
cross join
User user1_
where
member0_.name=user1_.name
Member(id=1, name=A, age=10)
Member(id=2, name=B, age=10)
- cross join을 한 후 같은 이름의 member가 정상적으로 조회가 되는 것을 확인할 수 있다.
조인 - ON
- JPA 2.1부터 지원하는 기능이다.
- 조인 대상을 필터링할 수 있다.
- 연관 관계없는 엔티티를 외부 조인할 수 있다.
조인 대상 필터링
- 조인을 하기 전 조인할 대상을 미리 필터링할 수 있다.
where로 조회할 시
Team team1 = new Team("teamMember1");
em.persist(team1);
Team team2 = new Team("teamMember2");
em.persist(team2);
em.persist(new Member("teamMember1", 10, team1));
em.persist(new Member("teamMember2", 13, team2));
em.persist(new Member("teamMember3", 15, team2));
em.flush(); em.clear();
List<Member> resultList = em.createQuery
("select m from Member m left join m.team t where t.teamName = m.name", Member.class)
.getResultList();
for (Member member : resultList) {
System.out.println(member);
}
- 우선 on절이 아닌 where 절로 team의 이름과 member의 이름이 같은 member를 조회해보자.
- 이때는 조인 후 where로 필터링하기 때문에 아래와 같이 두 개의 멤버만 조회된다.
Hibernate:
select
생략...
from
Member member0_
left outer join
Team team1_
on member0_.team_id=team1_.team_id
where
team1_.teamName=member0_.name
Member(id=3, name=teamMember1, age=10)
Member(id=4, name=teamMember2, age=13)
on으로 조회할 시
List<Member> resultList = em.createQuery
("select m from Member m left join m.team t on t.teamName = m.name", Member.class)
.getResultList();
for (Member member : resultList) {
System.out.println(member);
}
- 이렇게 on 절로 조회를 하게 되면 조인을 하기 전 team을 필터링하게 된다.
- team을 필터링 후 left 조인을 하여도 left 조인 특성상 member가 값을 가지고 있다면 member는 조회가 될 것이다.
- 그러므로 아래와 같이 모든 member가 출력되는 것을 알 수 있다.
Hibernate:
select
생략...
from
Member member0_
left outer join
Team team1_
on member0_.team_id=team1_.team_id
and (
team1_.teamName=member0_.name
)
Member(id=3, name=teamMember1, age=10)
Member(id=4, name=teamMember2, age=13)
Member(id=5, name=teamMember3, age=15)
- sql을 확인해보면 조인할 때 and 연산을 통해 같은 name을 가지고 있는 대상을 필터링하는 것을 알 수 있다.
연관관계가 없는 엔티티 조인
- 내부 조인은 원래 가능하지만 외부 조인은 Hibernate 5.1부터 가능하다.
내부 조인
em.persist(new User("A"));
em.persist(new User("B"));
em.persist(new Member("A", 10));
em.persist(new Member("B", 10));
em.persist(new Member("C", 10));
em.flush();
em.clear();
List<Member> result = em.createQuery
("select m from Member m inner join User u on m.name = u.name", Member.class)
.getResultList();
result.forEach(System.out::println);
- 연관관계가 없는 User와 Member를 on을 통해 내부 조인하였다.
Hibernate:
select
생략...
from
Member member0_
inner join
User user1_
on (
member0_.name=user1_.name
)
Member(id=3, name=A, age=10)
Member(id=4, name=B, age=10)
- sql을 확인해보면 name으로 내부 조인이 되는 것을 확인할 수 있고 User와 name이 같은 A, B만 조회되는 것을 알 수 있다.
외부 조인
List<Member> result = em.createQuery
("select m from Member m left join User u on m.name = u.name", Member.class)
.getResultList();
result.forEach(System.out::println);
- left (outer) join 으로 변경하여 실행해본다.
Hibernate:
select
생략...
from
Member member0_
left outer join
User user1_
on (
member0_.name=user1_.name
)
Member(id=3, name=A, age=10)
Member(id=4, name=B, age=10)
Member(id=5, name=C, age=10)
- left outer join이므로 Member가 모두 조회된 것을 확인할 수 있다.
'JPA' 카테고리의 다른 글
JPQL 서브쿼리, 조건식, 기본 함수 (0) | 2020.01.26 |
---|---|
JPQL: 기본문법과 프로젝션 (0) | 2020.01.24 |
JPA 연관관계 매핑 6. 영속성 전이(CASCADE)와 고아 객체 (0) | 2020.01.23 |
JPA 연관관계 매핑 5. 즉시 로딩과 지연 로딩 (0) | 2020.01.23 |
JPA 연관관계 매핑 4. 프록시 (0) | 2020.01.23 |