computer_study

[JPA] 14. 컬렉션과 부가 기능 본문

스터디/자바 ORM표준 JPA 프로그래밍

[JPA] 14. 컬렉션과 부가 기능

knowable 2022. 10. 5. 02:22

1. 컬렉션

JPA 명세에 자바 컬렉션에 대한 언급이 없기에 JPA 구현체에 따라 제공하는 기능이 다를 수 있다.

(책에선 하이버네이트 구현체를 기준으로)

 

JPA와 컬렉션

  • 하이버네이트는 엔티티를 영속 상태로 만들 때 컬렉션 필드를 하이버네이트에서 준비한 컬렉션으로 감싼다.
  • ex) ArrayList타입이 하이버네이트가 제공하는 PersistentBag타입으로 변경된다.
    • Collection, List -> PersistentBag으로 변경
      • ArrayList로 초기화하면 된다.
      • Private Collection<CollectionChild> collection = new ArrayList<CollectionChild>();
      • 엔티티 추가 시 중복된 엔티티가 있는지 비교하지 않고 단순히 저장만 하면 된다.
      • 엔티티를 추가해도 지연 로딩된 컬렉션을 초기화하지 않는다.
    • Set -> PersistentSet으로 변경
      • HashSet으로 초기화
      • private Set<SetChild> set = new HashSet<SetChild>();
      • Set은 엔티티를 추가할 때 중복된 엩티티가 있는지 비교해야 한다.
      • 엔티티를 추가할 때 로딩된 컬렉션을 초기화 한다.
    • List + @OrderColumn -> PersistentList로 변경
      • @OrderColumn을 추가하면 순서가 있는 특수한 컬렉션으로 인식 (데이터베이스에 순서 값을 저장해서 조회할 떄 사용)
      • 실무에서 사용하기엔 단점이 많다.
        • Comment를 INSERT할 경우 UPDATE하는 SQL이 추가로 발생한다.
        • List변경 시 연관된 많은 위치 값들을 변경해야 한다.
        • 컬렉션을 순회할 때 NullPointerException이 발생한다.

@OrderBy

  • @OrderColumn이 DB에 순서용 컬럼을 매핑해서 관리한다면 @OrderBy는 ORDER BY절을 사용해서 컬렉션을 정렬
  • 순서용 컬럼을 매핑하지 않아도 된다.

2. @Converter

  • 엔티티의 데이터를 변환해서 DB에 저장할 수 있다.
CREATE TABLE MEMBER (
    ID VARCHAR(255) NOT NULL,
    USERNAME VARCHAR(255),
    VIP VARCHAR(1) NOT NULL,
    PRIMARY KEY (ID)
)
@Entity
public class Member {
    
    @Id
    private String id;
    private String username;
    
    // DB에 저장되기 직전에 BooleanToYNConverter(Bollean을 YN으로 변환해주는 컨버터)가 동작하도록
    @Convert(converter=BooleanToYNConverter.class)
    private bollean vip;
    
    // Getter, Setter
    // ...
}
  • 모든 Boolean 타입에 컨버터를 적용할 수도 있다
    • @Converter(AutoApply = true) 사용
    • @Converter를 지정하지 않아도 모든 Boolean 타입에 대해 자동으로 컨버터가 적용된다.

3. 리스너

  • 엔티티의 생명주기에 따른 이벤트를 처리하기 위함

이벤트 종류

이벤트 적용 위치

  • 엔티티에 직접 적용
    • @PrePersist, @PostPersist등의 어노테이션으로 지정한 메소드가 이벤트 발생 시 마다 실행된다.
  • 별도의 리스너 등록
    • @EntityListeners(DuckListener.class) 와 같이 리스너를 사용할 수 있다
  • 기본 리스너 사용
    • META-INF/orm.xml에 default리스너로 등록하여 사용할 수 있다.
  • 여러 리스너 등록 시 이벤트 호출 순서
    • 기본 리스너
    • 부모 클래스 리스너
    • 리스너
    • 엔티티
  • 세밀한 설정
    • 이벤트를 잘 활용하면 대부분의 엔티티에 공통으로 적용하는 등록 일자, 수정 일자 처리 등에 대한 기록을 리스너 하나로 처리할 수 있다.
    • javax.persistence.ExcludeDefaultListeners -> 기본 리스너 무시
    • javax.persistence.ExcludeSuperclassListeners -> 상위 클래스 이벤트 리스너 무시

 

4. 엔티티 그래프

  • 엔티티 그래트의 기능은 엔티티 조회시점에 연관된 엔티티들을 함께 조회하는 기능
  • Named 엔티티 그래프
    • 엔티티 그래프를 정적으로 정의
    • @NamedEntityGraph로 정의
    • @NamedEntityGraph(name ="Order.withMember", attributeNodes = (@NamedAttributeNode("Member"))
      • name -> 엔티티 그래프의 이름을 정의
      • attributeNodes -> 함께 조회할 속성을 선택
    • Named 엔티티 그래프를 사용하기 위해선
      • 정의한 엔티티 그래프를 em.getEntityGraph("Order.withMember") 이런 식으로 찾아올 수 있다.
      • 조회 시에는 Hints 값으로 그래프를 주어 엔티티 그래프를 사용해 조회할 수도 있다.
      • Order-> OrderItem -> Item 이런식으로 조회하고 싶다면 subgraph를 사용하면 된다.
EntityGraph graph = em.getEntityGraph("Order.withMember");

Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

Order order = em.find(Order.class, orderId, hints);
  • 동적으로 정의하는 그래프
    • createEntityGraph()메소드를 사용해서 엔티티 그래프를 동적으로 구성할 수 있다
EntityGraph<Order> graph = em.createEntityGraph(Order.class) // 그래프를 통적으로 구성 가능
graph.addAttributeNodes("member"); // Order.member 속성을 엔티티 그래프에 포함
Subgraph<OrderItem> orderItems = graph.addSubgraph("orderItems"); // 서브그래프
orderItems.addAttributeNodes("item"); // 서브 그래프가 item 속성을 포함하도록

Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

Order order = em.find(Order.class, orderId, hints);

 

 

 

Comments