beadss / jpa-study

jpa슽터디입니다
1 stars 2 forks source link

14장 정리중 #38

Open joont92 opened 5 years ago

joont92 commented 5 years ago

컬렉션

JPA는 자바에서 기본으로 제공하는 Collection, List, Set, Map 컬렉션을 지원하고, 아래와 같은 상황에서 컬렉션을 사용할 수 있다.

하이버네이트는 엔티티를 영속상태로 만들 때 컬렉션 필드를 하이버네이트에서 준비한 컬렉션으로 감싸서 사용한다.
이는 하이버네이트가 컬렉션을 효율적으로 관기하기 위함이다.
하이버네이트는 본 컬렉션을 감싸고 있는 내장 컬렉션을 생성한 뒤, 이 내장 컬렉션을 사용하도록 참조를 변경한다.

Collection, List

중복을 허용하는 컬렉션이다.
하이버네이트에서 PersistentBag으로 래핑된다. 사용할 때는 ArrayList로 초기화하면 된다.

@OneToMany(mappedBy = "parent")
Collection<Child> children = new ArrayList<>();
// or
@OneToMany(mappedBy = "parent")
List<Child> children = new ArrayList<>();

중복을 허용하는 특성때문에 겍체를 추가할때 아무 조건검사가 필요없으므로, 지연로딩이 발생하지 않는다.
하지만 엔티티가 있는지 체크하거나 삭제할 경우 eqauls로 비교해야 하므로 지연로딩이 발생한다.

children.add(child); // no action  

children.contains(child); // Lazy loading occurs because of using equals
children.remove(child); // Lazy loading occurs because of using equals

Set

중복을 허용하지 않는 컬렉션이다.
하이버네이트에서 PersistentSet으로 래핑된다. 사용할 때는 HashSet으로 초기화하면 된다.

@OneToMany(mappedBy = "parent")
Set<Child> children = new HashSet<>();

중복을 허용하지 않으므로 객체를 추가할 때 마다 equals 메서드로 같은 객체가 있는지 비교한다. 즉 add 메서드만 수행해도 지연로딩이 발생한다.
참고로 HashSet은 해시 알고리즘을 사용하므로 equals와 hashCode를 같이 사용한다.

children.add(child); // eqauls + hashCode

children.contains(child); // eqauls + hashCode
children.remove(child); // eqauls + hashCode

List + @OrderColumn

순서가 있는 복수형 컬렉션을 의미하는데, 데이터베이스에 순서값을 저장해서 조회할 때 사용한다는 의미이다.

OrderColumn

위처럼 데이터베이스에 순서값을 함꼐 관리하는 테이블에 사용된다.

@Entity
class Board{
    @Id @GeneratedValue
    private Integer id;

    @OneToMany(mappedBy = "board")
    @OrderColumn(name = "POSITION")
    private List<Comment> comments = new ArrayList<>();
}

@Entity
class Comment{
    @Id @GeneratedValue
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;
}

List의 위치(순서)값을 POSITION이라는 컬럼에 저장하게 되는것이고, 이는 일대다 관계의 특성에 따라 다(N)쪽에 저장하게 된다.
아래는 사용예제이다.

Board board = new Board("title", "content");
em.persist(board);

Comment comment1 = new Comment("comment1");
comment1.setBoard(board); // POSITION 0
em.persist(comment1);

Comment comment2 = new Comment("comment1");
comment2.setBoard(board); // POSITION 1
em.persist(comment2);

어떻게보면 위치값을 알아서 관리해주니 편해보이지만 사실은 실무에서 사용하기에는 단점이 많다.


컨버터

@Converter를 사용하면 엔티티의 데이터를 변환해서 데이터베이스에 저장할 수 있다.
예를 들면 엔티티에는 boolean, 데이터베이스에는 YN 값을 저장하고 싶을 경우 정도가 있겠다.
(기본적으로 boolean 타입으로 지정하면 0,1로 저장된다)

기본 사용법

@Entity
class Member{
    @Id @GeneratedValue
    private Integer id;

    @Convert(converter=BooleanToYNConverter.class)
    private boolean useYn;
}

@Converter
class BooleanToYNConverter implements AttributeConverter<Boolean, String>{
    @Override
    public String convertToDatabaseColumn(Boolean attribute){
        return (attribute != null && attribute) ? "Y" : "N";
    }

    @Override
    public Boolean convertToEntityAttribute(String dbData){
        return "Y".eqauls(dbData);
    }
}

보다시피 간단하다.
AttributeConverter를 구현하면서 제네릭으로 엔티티 컬럼 타입, 데이터베이스 컬럼 타입을 주고,
아래 2개의 메서드를 오버라이드 해주면 된다(엔티티->데이터베이스, 데이터베이스->엔티티)

클래스 레벨 설정

클래스 레벨에도 설정할 수 있다. 단 이때는 attrbuteName 속성을 사용해서 어떤 필드에 컨버터를 적용할 지 명시해야한다.

@Entity
@Converter(converter = BooleanToYNConverter.class, attributeName = "useYn")
class Member{
    // ...
}

글로벌 설정

모든 Boolean 타입에 설정하고 싶을 경우 아래와 같이 직접 컨버터에 명시해주면 된다.

@Converter(autoApply = true)
class BooleanToYNConverter implements AttributeConverter<Boolean, String>{
    // ...
}

이렿게 하면 엔티티에 따로 컨버터를 지정해주지 안항도 자동으로 컨버팅 된다.