반응형

1. 개요

 - 프로젝션과 JPA 페이징 API에 대한 개념을 정리한다.

 


2. 프로젝션이란?

 - SELECT 절에 조회할 대상을 지정하는 것을 말한다.

 - 조회된 대상은 모두 영속성 컨텍스트에서 관리된다.


3. 조회할 대상 (프로젝션 대상)

 - 조회할 대상은 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자, 등 기본 데이터 타입)이 있다. 각 타입의 의미는 예제를 통해 쉽게 이해 가능하다.

	SELECT m FROM Member m // Member 엔티티를 조회하는 엔티티 프로젝션

	SELEECT m.team FROM Member m // Member 엔티티와 관계를 맺고 있는 Team 엔티티를 조회하는 엔티티 프로젝션

	SELECT m.address FROM Member m // Member 엔티티에 임베디드 타입인 address를 조회하는 임베디드 타입 프로젝션

	SELECT m.username, m.age FROM Member m // 기본 데이터 타입들을 조회하는 스칼라 타입 프로젝션

4. 여러 값 조회하기

 - 한 로우에 여러 값을 조회한다는 것은 반환 값이 명확하지 않다는 뜻이다. 예를들어 위의 스칼라 타입 프로젝션 같은 경우 username이라는 String과, age라는 int형을 조회하는데 두 값을 한번에 받을 수 있는 타입이 존재하지 않기 때문이다.

 - 이처럼 반환 값이 명확하지 않을 경우 1차적으로 Query 타입으로 조회하게 된다.

 - getResultList 사용 시에는 결과를 ArrayList<Object>로, getSingleResult 사용 시에는 결과를 Object 형태로 리턴받는다.

 - 조회한 로우의 각 속성들을 조회하고 싶다면 Object를 Object[]로 캐스팅하여 result[0], result[1]과 같이 조회해야한다.

Query.getResultList() 결과

 - 이러한 매커니즘을 활용하여 여러 값을 조회하는 방법은 크게 3가지가 있다.

 1) 앞서 언급한 Query 타입으로 조회하는 방법

 2) 반환 값을 Object[]로 명확히 하여 TypeQuery 타입으로 조회하는 방법

 3) new 명령어로 조회하는 방법

 

4.1. Query 타입 조회

// Query 타입 조회 방법
	Object result2 = 
		em.createQuery("select m.username, m.id from Member m where m.id = 1L").getSingleResult();

	Object[] objects = (Object[])result2;
	System.out.println(objects[0]); // username
	System.out.println(objects[1]); // id

 - Query 타입으로 조회 시 Object로 받게 되며, 각 속성에 접근하기 위해 반드시 Object 배열로 캐스팅해야하고 배열 번호로 접근해야 하는 단점이 있다.

 

4.2. TypedQuery 타입 조회(= Object[] 조회)

	// TypedQuery 타입 조회 방법 == Object[] 타입 조회
	Object[] result3 = 
		em.createQuery("select m.username, m.id from Member m where m.id = 1L",Object[].class).getSingleResult();
        System.out.println(result3[0]); // username
 	System.out.println(result3[1]); // id

  - TypedQuery 타입으로 조회 시 createQuery 메서드에서 Object[] 로 캐스팅 작업을 먼저 하기때문에 추가적인 캐스팅 코드를 작성하지 않는다. 하지만 배열 번호를 통한 접근은 그리 좋지 않아보인다.

 

4.3. new 생성자를 통한 조회

	// new 생성자를 통한 조회
	MemberDto result3 = 
		em.createQuery("select new jqpl.MemberDto(m.id, m.username) from Member m where m.id = 1L",MemberDto.class).getSingleResult();
        System.out.println(result3.getId()); // username
        System.out.println(result3.getUsername()); // id

	
	...
	// MemberDto.java
	public class MemberDto {

    	private Long id;

    	private String username;

    	public MemberDto(Long id, String username) {
        	this.id = id;
        	this.username = username;
    	}
		...
		// getter, setter
	}

 - new 생성자를 사용할 경우 JPQL의 조회 형태에 맞는 생성자를 가진 DTO 클래스를 생성해야 한다.

 - 배열의 번호를 통한 접근이 아니고 캐스팅 코드가 없는 깔끔한 조회 방식이나 JPQL에 DTO에 대한 풀 패키지 경로를 입력해야하는 단점이 있다. 하지만 이는 쿼리 DSL에서 극복되었으므로 이 방식을 사용하는 것이 권장된다.

 


5. 페이징 API

 - JPA는 페이징 처리 시 setFirstResult, setMaxResults라는 메서드로 추상화한다.

setFirstResult(int startPosition) // startPosition : 조회할 시작 위치
setMaxResults(int maxResult) // maxResult : 조회할 데이터 수

 - 내부적으로 동작되는 쿼리는 JPA에 설정한 Database 방언에 맞게 실행된다.

 


6. 페이징 API 예제

 - 나이 오름차순으로 조회한 멤버 리스트들 중 0번째부터 시작해 10개의 데이터를 조회하는 예제이다. setFirstResult(0), set MaxResults(10)으로 하여 간단히 조회 가능하다.

    for(int i =0 ; i< 100 ; i++){
        Member member = new Member();
        member.setUsername("멤버"+i);
        member.setAge(i);
        em.persist(member);
    }

	// 0번째부터 시작해서 10개의 데이터를 조회한다.
    List<Member> list = 
		em.createQuery("select m from Member m order by m.age asc",Member.class)
            .setFirstResult(0)
            .setMaxResults(10)
            .getResultList();

    for(Member member : list){
        System.out.println(member.toString());
    }

	//실행결과
	Member{id=1, username='멤버0', age=0}
	Member{id=2, username='멤버1', age=1}
	Member{id=3, username='멤버2', age=2}
	Member{id=4, username='멤버3', age=3}
	Member{id=5, username='멤버4', age=4}
	Member{id=6, username='멤버5', age=5}
	Member{id=7, username='멤버6', age=6}
	Member{id=8, username='멤버7', age=7}
	Member{id=9, username='멤버8', age=8}
	Member{id=10, username='멤버9', age=9}

7. 참고

 - JAVA ORM 표준 JPA 프로그래밍 - 김영한

반응형

+ Recent posts