JPA_2

Seung·2023년 7월 5일
0
post-thumbnail

프록시와 연관관계 관리

프록시

회원과 팀을 함께 출력
public void printUserAndTeam(String memberId) {
    Member member = em.find(Member.class, memberId);
    Team team = member.getTeam();

    System.out.println("회원 이름 : " + member.getUserName());
    System.out.println("소속팀 : " + team.getName());
}
회원만 출력
public String printUser(String memberId) {
    Member member = em.find(Member.class, memberId);

    System.out.println("회원 이름 : " + member.getUserName());
}

회원과 팀이 연관관계일 경우 회원만 조회시에는 팀의 정보도 함께 데이터베이스에서 조회는 비효율적
JPA는 엔티티가 실제 사용될 때까지 데이타베이스 조회를 지연하는 방법을 제공.

  • em.find() vs em.getReference() 차이
    find() : 데이터베이스를 통해서 실제 엔티티 객체 조회
    getReference() : 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체조회
  • 특징
    1. 실제 클래스를 상속받아서 만들어짐 (실제 클래스와 겉 모양이 같다.)
    2. 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용
    3. 프록시 객체는 실제 객체의 참조를 보관
    4. 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출
    5. 영속성 컨텍스트에 찾는 엔티티가 존재시 getReference()를 호출해도 실제 엔티티 반환

즉시로딩과 지연로딩

  • 지연로딩
    연관된 엔티티는 프록시로 조회
    프록시로 조회된 엔티티는 실제 사용시에 데이터베이스에서 조회

    @Entity
    public class Member {
       //(fetch = FetchType.LAZY)
       @ManyToOne(fetch = FetchType.LAZY)
       @JoinColumn(name = "TEAM_ID")
       private Team team;
    
    }
  • 즉시로딩
    연관된 엔티티를 한번에 조회
    JPA 구현체는 가능하면 조인을 사용해서 SQL한번에 조회

    @Entity
    public class Member {
    
       //(fetch = FetchType.EAGER)
       @ManyToOne(fetch = FetchType.EAGER)
       @JoinColumn(name = "TEAM_ID")
       private Team team;
    }
  • 주의사항

    1. 가급적 지연로징(LAZY)만 사용할 것
    2. 즉시 로딩을 적용시에는 예상치 못한 SQL이 발생
    3. 즉시로딩은 JPQL에서 N+1 문제가 발생
    4. @ManyToOn, @OneToOne은 기본값이 즉시로딩이니 변경 필수

    N+1 문제란? (즉시로딩사용시 JPQL)
    하나의 쿼리를 실행하는데 N개의 쿼리가 더 실행되는 현상
    JPQL사용시에 즉시로딩은 SQL로 번역된 후 연관관계 엔티티도 추가적으로 쿼리가 발생함
    예를들어, 각각 다른 팀에 소속된 회원 2명을 JPQL로 조회시 즉시로딩일 경우 회원정보조회 하는 쿼리 한개와 회원별의 팀정보 쿼리2개가 추가로 더 발생한다.

영속성 전이: CASCADE

  • 특정 엔티티를 영속 상태로 만들 떄 연관된 엔티티도 함께 영속 상태로 만들사용
    ex) 부모 엔티티를 저장시에 자식 엔티티도 함께 저장
    @OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
  • CASCADE 종류
    ALL : 모두 적용
    PERSIST : 영속
    REMOVE : 삭제
    MERGE : 병합
    REFRESH
    DETACH
  • 고아객체
    부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동삭제
    orphanRemoval=true
    OneToMany 연관에서 부모 엔티티의 컬렉션 등에서 자식 엔티티가 삭제될 때 참조가 끊어지므로 DB 레벨에서도 삭제.
    @OneToMany(mappedBy = "parent", orphanRemoval = true)

값타입

  • 기본값 타입
    자바 기본타입, 래퍼 클래스, String
    생명주기를 엔티티에 의존
    기본값 타입은 절대 공유X

  • 임베디드 타입(embedded type, 복합 값타입)
    새로운 값 타입을 직접 정의 가능
    주로 기본 값 타입을 모아 만들어서 복합 값 타입이라고 함

    예를들어 회원 엔티티에 이름,근무기간,집주소를 가질경우
    근무기간(시작일,종료일)이나 집주소(도시,우편번호,주소)을 임베디드 타입으로 구성

    @Embeddable : 값 타입을 정의하는 곳에 표시
    @Embedded : 값 타입을 사용하는 곳에 표시
    기본 생성자 필수

    //값타입 사용
    @Entity
    public class Member {
     ...
     @Embedded // 값 타입을 사용
     private Period workPeriod;
     ...
    }
    // 값 타입 정의
    @Embeddable 
    @NoArgsConstructor // 기본 생성자 필수
    public class Period {
        private LocalDateTime startDate;
       private LocalDateTime endDate;
       
       public boolean isWork(Date date){
           // 별도의 메소드 정의가 가능
       }
    }
  • 컬렉션 값 타입(collection value type)

객체지향 쿼리 언어

JPQL(Java Persistence Query Language)

  • JPQL은 객체지향 쿼리언어이다. 따라서 테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리한다.
    JPQL은 SQL을 추상화해서 특정 데이터베이스에 의존하지 않는다.

  • JPQL 문법

    String jpql = "select m From Member m where m.age > 19";
    List<Member> result = em.createQuery(jpql,Member.class)
                .getResultList();

    엔티티와 속성은 대소문자 구분O (Member,age)
    JPQL 키워드는 대소문자 구분X (select,from,where)
    엔티티 이름 사용,테이블명이 아님(Member는 엔티티명)
    별칭은 필수(m), as는 생략가능

  • TypeQuery / Query
    TypeQuery : 반환 타입이 명확할때 사용

    TypeQuery<Member> query = em.createQuery("select m from   Member m", Member.class);

    Query : 반환 타입이 명확하지 않을때 사용

    //반환타입이 String,int 명확X
    Query query = em.createQuery("select m.age,m.name from  Member m");
  • 결과조회 API
    query.getResultList() : 결과가 하나 이상일 경우 리스트 반환
    결과가 없으면 빈 리스트 반환
    query.getSingleResult() : 결과가 정확히 하나인 경우, 단일 객체 반환
    결과 없으면 : NoResultException
    결과가 둘이상시 : NonUniqueResultException

  • 파라미티 바인딩
    이름기준

    select m from Member m where m.name=:name
    query.setParameter("name",nameParam);

    위치기준

    select m from Member m where m.name=?1
    query.setParameter(1,nameParam);

    이름기준으로 쓸 것을 추천 (유지보수시 용이)

  • 페이징 API
    setFirstResult(int startPosition) : 조회시작 위치(0부터 시작0
    setMaxResults(int maxResult) : 조회할 데이터 수

    String jpql = "select m from Member m order by m.nmae desc";
    List<Member> resultList = em.createQuery(jpql, Member.class)
        .setFirstResult(10)
        .setMaxResults(20)
        .getResultList();

    JPA는 데이터베이스방언을 지원하여 데이터베이스별 페이징쿼리를 자동번역

  • 조인
    내부조인 : selelct m from Member m [INNER] join m.team t
    외부조인 : selelct m from Member m left [OUTER] join m.team t
    세타조인 : selelct count(m) from Member m,Team t where m.name = t.name

  • 조인 on 절
    조인대상 필터링

    회원과 팀을 조인하면서 팀이름이 'A'인 팀만 조회
    JPQL : select m,t from Member m left join m.team t on t.name = 'A'
    
    SQL : select m.*,t.* from Member m left join Team t on m.TEAM_ID = t.id and t.name = 'A'

    연관관계 없는 엔티티 외부 조인

    회원 이름과 팀의 이름이 같은 대상 외부조인
    JPQL : select m,t from Member m left join Team t on m.name = t.name
    
    SQL : select m.*,t.* from Member m left join Team t on m.name = t.name
  • 서브쿼리
    기본적은 SQL과 유사
    JPA는 WHERE,HAVING 절에서만 사용 가능
    SELECT 절도 가능 (하이버네이트에서 지원)
    FROM절은 불가능(조인으로 해결)

  • JPQL 타입 표현
    문자 : 'HELLO', 'She''s'
    숫지 : 10L, 10D, 10F
    Boolean : TRUE, FALSE
    ENUM : jpabook.MemberType.Admin(패키지명 포함)
    엔티티타입 : TYPE(m) = Member (상속관계에서 사용)

  • 경로 표현식
    점(.)을 찍어 객체 그래프 탐색

    select m.username ->상태 필드
    from Member m 
    join m.team t     ->단일 값 연관 필드
    join m.orders o   ->컬렉션 값 연관필드
    where t.name = 'TEAM'

    상태 필드 : 경로 탐색의 끝
    단일 값 연관 경로 : 묵시적 내부조인시 발생,탐색 O
    컬렉션값 연관 경로 : 묵시적 내부조인시 발생, 탐색X

    명시적 조인: join 키워드 직접 사용
    select m from Member m join m.team t
    묵시적 조인 : 경로 표현식에 의해 묵시적 으로 조인 발생
    select m.team from Member m

페치 조인(fetch join)

JPQL에서 성능 최적화를 위해 제공하는 기능
연관된 엔티티나 컬렉션을 SQL 한번에 함께 조회하는 기능
일반적인 JPQL 조인과 다르게 별칭 없음 (페치 조인은 별칭X)
컬렉션을 페치조인하면 페이징API 사용 불가능

[LEFT [OUTER] | INNER] JOIN FETCH 조인경로

JPQL
select m from Member m join fetch m.team
SQL
SELECT M.*,T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID=T.ID
  • 페치조인과 DISTINCT
    SQL에 중복결과 제거하는 DISTINCT 명령어 추가
    애플리케이션에서 엔티티 중복 제거
profile
한번 해봅시다.

0개의 댓글