본문 바로가기
학습/DB

프로젝션(엔티티, 임베디드, 스칼라타입)

코동이 2021. 10. 6.

SELECT 절에 조회할 대상을 지정한다. 일반 SQL 조회는 단순히 원하는 칼럼을 조회하지만, JPA는 엔티티, 임베디드, 스칼라타입등 다양한 리턴타입이 조회대상에 올 수 있다.

 

*목차

1. 프로젝션 종류

2. 연관 컬렉션 조회와 한계

 - 명시적 내부 조인 권장

3. 프로젝션 여러 값 조회

 

프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)

 

 

1. 프로젝션 종류

 

//엔티티 프로젝션
SELECT m FROM Member m

 

 

//엔티티 프로젝션(내부조인 발생)
SELECT m.team FROM Member m

 

 

//임베디드 타입 프로젝션
SELECT m.address FROM Member m

 

 

//스칼라 타입 프로젝션
SELECT m.username, m.age FROM Member m

 

 

왜 엔티티 프로젝션을 따로 용어로 구분지을까?

 

엔티티를 프로젝션 하면, 해당 결과는 엔티티 영속성에서 관리가 된다. 따라서, 값을 수정하면 영속성 컨텍스트에서 관리되기 때문에 커밋(flush)되면 더티 체킹으로 자동 반영되기 때문이다.

 

Member member1 = new Member();
member1.setName("memberA");
member1.setAge(15);
em.persist(member1);

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

List<Member> result = em.createQuery("select m from Member m", Member.class)
.getResultList();

Member findMember = result.get(0);
findMember.setAge(20);

 

 

2. 연관 컬렉션 조회와 한계 (리스트 컬렉션)

 

List<Team> result = em.createQuery("select m.team from Member m", Team.class)
.getResultList();

 

Hibernate: 
    /* select
        m.team 
    from
        Member m */ select
            team1_.id as id1_5_,
            team1_.name as name2_5_ 
        from
            Member member0_ 
        inner join
            Team team1_ 
                on member0_.TEAM_ID=team1_.id

단순히 m.team을 조회했을 뿐인데, 조인을 활용한 복잡한 쿼리가 나간다. 단순하더라도 우리가 작성한 JPA를 보고  따라서, 프로젝션으로 연관된 엔티티를 조회하는 것은 권장하지 않는다.

 

 

* 명시적 내부 조인 권장

 

List<Team> result = em.createQuery("select t from Member m join m.team t", Team.class)
.getResultList();

 

연관컬렉션 조회와 결과가 같다. 명시적 조인을 해야 개발자가 코드를 파악하기 쉽기 때문에 명시적 내부 조인을 권장한다.

 

 

3. 프로젝션 여러 값 조회

 

1. Query 타입으로 조회 ( Query query = ...)

 

query는 타입이 없기 때문에 가능하다. 단, Object이기 때문에 타입 캐스팅을 해야한다.

 

List resultList = em.createQuery("select m.name, m.age from Member m")
.getResultList();

Object o = resultList.get(0);
Object[] result = (Object[]) o;
System.out.println(result[0]);
System.out.println(result[1]);

 

resultList는 List가 리턴형일뿐, 어떠한 객체타입인지 모른다. 왜냐하면 Object 타입이기 때문이며, Object[]을 통해 각각 조회하고자하는 칼럼을 배열에서 꺼내야한다.

 

 

2.Object[] 타입으로 조회

 

List<Object[]> resultList = em.createQuery("select m.name, m.age from Member m")
.getResultList();

Object[] result = resultList.get(0);
System.out.println(result[0]);
System.out.println(result[1]);

 

List에 타입을 넣어줄 수 있으며, 조회시에 좀 더 코드양이 줄어들지만, 이 방법도 한계가 있다.

 

 

3. new 명령어로 조회

 

단순값을 DTO로 바로 조회한다.

 

List<MemberDto> resultList = em.createQuery("select new domain.MemberDto(m.name, m.age) from Member m")
.getResultList();

MemberDto result = resultList.get(0);
System.out.println(result.getName());
System.out.println(result.getAge());

 

패키지 명을 포함한 전체 클래스명을 입력하며 new로 해준다. 리턴 타입은 MemberDto으로 한다.

순서와 타입이 일치하는 생성자가 필요하다. 한계는 패키지명이 길어질수록 쿼리가 복잡해진다.

반응형

'학습 > DB' 카테고리의 다른 글

서브쿼리  (0) 2021.10.06
조인  (0) 2021.10.06
경로 표현식  (0) 2021.10.05
JPA에서 Dirty Check 방지하기  (0) 2021.10.02
영속성 컨텍스트로 발생하는 이슈  (0) 2021.10.01