JPQL
DTO Select
List<MemberDto> result = em.createQuery("select new jpql.MemberDto(m.username,m.age) from Member m", MemberDto.class)
.getResultList();
for (MemberDto memberDto : result) {
System.out.println("Username = " + memberDto.getUsername());
System.out.println("age = " + memberDto.getAge());
}
new 명령어를 사용하여 패키지를 포함한 전체클래스명을 입력하여 사용한다.
순서와 타입이 일치하는 생성자가 필요하다.
페이징
List<Member> result = em.createQuery("select m from Member m order by m.age desc ", Member.class)
.setFirstResult(10) //시작위치
.setMaxResults(10) // 데이터 수
.getResultList();
System.out.println("result = " + result.size());
for (Member member : result) {
System.out.println("member = " + member);
}
setFirstResult(int startPosition):조회 시작 위치
setMaxResults(int maxResult):조회할 데이터 수
경로표현식(단일값 연관경로)
//String query = "select m.team from Member m";
String query = "select t from Member m join m.team t "; // join을 하지않아도 묵시적 inner join이 발생하지만 명시적 조인 사용해야함
List<Team> result = em.createQuery(query, Team.class)
.getResultList();
query에 명시적 join을 하지 않아도 같은 값이 조회되지만 향후 유지보수를 위해 묵시적 join은 지양해야 한다.
경로표현식(컬렉션값 연관경로)
String query = "select m.username from Team t join t.members m "; // 명시적 조인을 통해서 별칭을 얻어야 탐색이 가능하다.
//String query = "select t.members from Team t "; // 탐색 불가능 쿼리
List<String> result = em.createQuery(query, String.class)
.getResultList();
컬렉션값을 탐색하기 위해서는 명시적 join을 통해서 별칭을 얻어야 탐색이 가능하다.
FetchJoin(지연로딩전략)
DB
(1) fetchjoin을 사용하지 않았을 때 문제점
String qeury = "select m from Member m"; //fetch join을 사용하지 않았을 때
List<Member> result = em.createQuery(qeury, Member.class)
.getResultList();
for (Member member : result) {
System.out.println("member = " + member.getUsername() + "," + member.getTeam().getName() );
}
지연로딩 전략 시 fetchjoin을 사용하지 않으면 Team객체가 프록시객체로 조회되고. 그 후 Team이 사용되는 시점에 Team이 조회되면서 영속성 컨텍스트에 저장된다. 첫번째 TeamA를 조회할 때는 쿼리가 나가면서 영속성 컨텍스트에 저장된다 . 그 후 두번쨰 팀A가 조회될 때에는 영속성컨텍스트의 정보를 이용하므로 쿼리가 조회되지 않는다. TeamB는 영속성 컨텍스트에 저장되어있지 않으므로 TeamB를 조회하는 쿼리가 나가는 것을 볼 수 있다.
(2) fetchjoin 사용
String fetch_query = "select m from Member m join fetch m.team";
List<Member> fetch_result = em.createQuery(fetch_query,Member.class)
.getResultList();
for (Member member : fetch_result) {
System.out.println("member = " + member.getUsername() + "," + member.getTeam().getName() );
}
fetchjoin을 사용할 경우 Member를 조회할 때 Team에 대한 정보도 함께 영속성 컨텍스트에 저장된다 . Team 정보가 영속성 컨텍스트에 저장되어 있으므로 Team이 사용되는 시점에 쿼리가 나가지 않는 것을 볼 수 있다.
(3) 일대다 fetchjoin 사용 - distinct 미사용
String collection_fetch_query = "select t from Team t join fetch t.members";
List<Team> collection_fetch_result= em.createQuery(collection_fetch_query,Team.class)
.getResultList();
for (Team team : collection_fetch_result) {
System.out.println(team.getName() + ":" +team.getMembers().size() + "명");
}
Team을 조회하여 Team이름과 Team에 속해있는 Member들의 수를 조회하려고 한다. 결과를 보면 Team A가 중복된 것을 볼 수 있는데 쿼리를 보면 Member와 Team을 조인하기 때문에 member들 중 TeamA에 속해 있는 Member가 두 명이므로 데이터가 증폭되는 문제점 있다.
(4) 일대다 fetchjoin 사용 - DISTINCT 사용
String collection_fetch_query = "select distinct t from Team t join fetch t.members";
List<Team> collection_fetch_result= em.createQuery(collection_fetch_query,Team.class)
.getResultList();
for (Team team : collection_fetch_result) {
System.out.println(team.getName() + ":" +team.getMembers().size() + "명");
}
일대다 fetchjoin에서 distinct를 사용하면 같은 식별자를 가진 Team 객체를 제거해 주어 Team 정보가 중복되는 것을 방지할 수 있다.
(5) fetchjoin의 특징과 한계
둘 이상의 컬렉션에는 fetchjoin이 불가능하다.
컬렉션을 fetchjoin 하면 메모리에서 페이징을 하기 때문에 지양해야 한다.
fetchjoin 대상에서 별칭을 줄 수 있지만 fetchjoin 대상에서 연관된 객체를 또 fetchjoin 할 때만 사용하자.
NamedQuery
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username "
)
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private int age;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
List<Member> result = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "회원1")
.getResultList();
for (Member member : result) {
System.out.println(member);
}
Entity를 선언하는 class에 @NamedQuery 어노테이션을 사용하여 선언가능하다.
애플리케이션 로딩 시점에 query를 검증한다.
애플리케이션 로딩 시점에 초기화 후 재사용한다.
Bulk 연산
int resultCount = em.createQuery("update Member m set m.age=20")
.executeUpdate();
query 한 번으로 여러 테이블의 column을 변경 가능하다.
executeUpdate()의 결과는 영향받은 엔티티수를 반환한다.
Bulk 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리 한다.
데이터베이스에 직접 쿼리 하기 때문에 Bulk 연산을 수행하면 영속성 컨텍스트를 초기화해주어야 한다.