객체지향 쿼리 언어 - 프로젝션(SELECT)

도윤·2024년 3월 22일
post-thumbnail

프로젝션(SELECT)

  • SELECT 절에 조회할 대상을 지정하는 것.
  • 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)
  • SELECT m FROM Member m -> 엔티티 프로젝션
Member member = new Member();
member.setUsername("member1")
member.setAge(10);
em.persist(member);
List<Member> result = em.createQUery("select m from Member m", Member.class)
						.getResultList();

일반적인 조회 방법은 여태 싸웠던 방식 그대로 사용하면 된다. 근데 여기서 한 가지 의문점이 든다. 저렇게 다건을 조회해와도 영속성 컨텍스트에 올라갈까?

em.flush();
em.clear();

List<Member> result = em.createQUery("selecdt m from Member m", Member.class)
							.getResultList();
                            
Member findMember = result.get(0);

처음 예제에서 추가로 코드를 넣어줬다. 먼저 영속성 컨텍스트를 모두 비우고 Member 를 조회한다. 그리고 나서 Member를 하나 가져오고 그 Member 의 Age 를 바꿔준다. 만약 UPDATE 쿼리가 나간다면 영속성 컨텍스트에서 관리자 되고 있는 것이고, 나가지 않는다면 영속성 컨텍스트에 없다는 말이다.

실행 후 출력 로그를 확인해보면 UPDATE 쿼리가 잘 나가는 것을 볼 수 있다. JPA 에서 1개든 10개든 100개든 조회해오는 모든 데이터는 영속성 컨텍스트로 올라간다.

  • SELECT m.team FROM Member m -> 엔티티 프로젝션
List<Team> result = em.createQuery("select m.team from Member m", Team.class)
					.getResultList();

코드를 위와 같이 수정한다. 조회를 해 오는 데이터가 m.team 이므로 Team.class 로 변경해줘야 한다. 그리고 실행을 해보면 JOIN 쿼리가 나간다.

뭔가 나는 JPQL 을 심플하게 작성했는데 쿼리가 복잡하게 나간다.좋은 기능 같지만 JPQL 도 최대한 쿼리와 비슷하게 작성해야 함이 맞다. JOIN 의 경우엔 성능과 직접적으로 연관되어 있는 기능이기도 하고 튜닝도 할 수 있기 때문에 한 눈에 보여야 한다. 그래서 코드를 아래와 같이 수정해주면 된다.

List<Team> reulst = em.createQuery("select m.team from Member m join m.team t",
Team.class)
 			.getReulstList();
  • SELECT m.address FROM Member m -> 임베디드 타입 프로젝션
List<Address> result = em.createQuery("select o.address from Order o", Address.class)

				 . getReulstList();

임베디드 타입 프로젝션은 특이한 점은 한계점이 존재한다.

List<Address) result = em.createQuery("select a from Address a", Address.class)
							.getResultList();
  • SELECT m.username, m.age FROM Member m -> 스칼라 타입 프로젝션
em.createQUery("select m.username, m.age from Member m")
				.getReulstList();

이 스칼라 타입 프로젝션이 일반 SQL 의 스칼라 프로젝션 타입과 비슷하다. 내용이 많으므로 {여러 값 조회} 에서 풀겠따.

  • DISTINCT
em.createQuery("select distinct m.username, m.age from Member m")
    .getReulstList();

SELECT 다음에 DISTINCT 키워드를 넣어주면 된다.

여러 값 조회

em.createQuery("select distinct m.username, m.age from Member m")
			.getResultList();

스칼라 타입에는 지금 username 과 age 두 개의 필드가 존재한다. 이 데이터를 어떻게 가져와야 할까?

  • Query 타입 조회
List resultList = em.createQuery("select distinct m.username, m.age from Member m")
			.getResultList();

Query 타입의 반환 값은 List이다. List 에는 특정 타입을 넣을 수 없으니 Object 로 둘려준다. 그래서 값을 가져 올때는 아래와 같이 형변환을 통해 값을 가져와야 한다.

  • Object[] 타입으로 조회
List<Object[]> resultList = em.createQuery("select distinct m.username, m.age from Member m")
					.getResultList();
                    
Object[] result = resultList.get(0);
System.out.println("result = " + result[0]);
System.out.println("result = " + result[1]);

List 제네릭에 Object[] 를 넣어주면 형변환 작업을 생략할 수 있다.

  • new 명령어로 조회

    • 이게 제일 깔끔한 방법이다.
    • 단순 값을 DTO 로 바로 조회
    • 패키지명을 포함한 전체 클래스 명 입력
    • 순서와 타입이 일치하는 생성자가 필요하다.

    먼저 MemberDTO 클래스를 생성한다.

그리고 실행 클래스에서 코드를 수정해준다.

List resultList = em.createQuery("select new jpql.MEMBERDTO(m.username, m.age) from Member m", MemberDTO.class)
			.getResultList();

MemberDTO memberDTO = reulst.get(0);
System.out.println("memberDTO = " + memberDTO.getUsername());
System.out.println("memberDTO = " + memberDTO.getAge());

뭔가 원하는 대로 작동이 되지만 뭔가 쿼리에 구구절절 패키지명을 적어야 하는게 맞나 싶기도 하다. 근데 추후 QueryDSL 을 사용하면 이런 불편한 부분을 극복할 수 있게 된다.

참고 : 자바 ORM 표준 JPA 프로그래밍 - 기본편

profile
기록은 기억을 이긴다⭐

0개의 댓글