OneToMany 관계 맵핑
Bag으로 맵핑하기
- 1대다 관계 맵핑하기에서는 Set으로 양방향 관계를 맵핑했었다.
- Bag은 1대다 양방향 관계를 맵핑하는 가장 효율적인 콜렉션이다. 왜? index도 없고, 중복도 허용하기 때문에, 그냥 넣기만 하면 된다.
- 그러나 2개의 bag 타입 콜렉션을 동시에 eaget-fetching 할 수 없다.(13장에서 다룬다.)
- Bag 타입을 사용하려면 변수의 타입은 Collection, 구현체는 ArrayList를 사용한다.
- 중복 데이터를 넣는 일은 코딩을 잘 해서 방지하면 되지만, 어차피 mappedby 속성(inverse)를
Collection에 걸어두었기 때문에, 여러번 추가해도 SQL은 실행되지 않는다.(그래도.. 메모리 상에서는 콜렉션에
들어갈텐데, 이러면, 안 되지 않을까.. 흠)
Item.java
@OneToMany(mappedBy = "item")
private Collection<Bid> bids = new ArrayList<Bid>();
Bidjava
@ManyToOne
private Item item;
List로 맵핑하기
- 콜렉션에 있는 Entity들의 index가 필요하다면, List로 맵핑하면 된다.
- 콜렉션을 맵핑할 때, 콜렉션의 엔티티는 index를 가지고 있을 추가적인 컬럼을 설정한다.(@IndexColumn)
- 양방향 설정할 때, 콜렉션에 index가 있는 경우(List, Map, 배열)에는 One쪽을 무시(inverse)한다.
- 왜? 콜렉션이 DB를 수정할 때 필요로 하는 정보(index)를 가지고 있기 때문에, 콜렉션을 기준으로
DB를 수정해야 한다. -> Bid테이블에 Bid_Index 컬럼이 있는데, 이 안에 넣을 값은 Item에 있는
List<Bid> 콜렉션에 넣어야 알 수 있자나. 그러니까 Bid를 기준으로 SQL을 만들 수 없지. 콜렉션에 넣을
때(item.getBids().add(bid)) Bid_Index에 넣을 값을 알 수 있으니까, 콜렉션을 기준으로 SQL을
생성하고, 그 반대쪽 SQL은 발생하지 않도록 inverse 해줘야 해.
- 따라서 Item과 Bid의 관계에서 Bid쪽의 변화를 무시해야 하는데, @ManyToOne에는 mappedBy 속성이 없다. 그래서 꼼수가 필요하다.
Item.java
@OneToMany
@JoinColumn(name="ITEM_ID", nullable = false)
@IndexColumn(name = "BID_POSITION")
private List<Bid> bids = new ArrayList<Bid>();
Bid.java
@ManyToOne
@JoinColumn(name="ITEM_ID", updatable=false, insertable=false)
private Item item;
- item.getBids().add(bid); 이렇게 할 때 SQL(update Bid set ITEM_ID=?, BID_POSITION=? where id=?)을 날린다.
- Bid 와 Item에 설정한 JoinColumn은 같은 이름이어야 한다. 둘 다 Many쪽(여기서는 Bid)에 있는 외례키 컬럼을 나타낸다.
- JPA는 indexed list를 지원하지 않기 때문에, 하이버네이트의 @IndexColumn 애노테이션을 사용한다.
Join Table로 맵핑하기
- OneToMany관계가 optional인 경우에 사용하면 좋다. 그러지 않고, 외례키 컬럼이 Null을 허용하도록 해도 되긴 되지만 비추한다.
- OneToOne관계를 Join Table로 맵핑한 것과 비슷하지만, 그 때는 두 개의 키를 모두
unique로 설정했지만, 이번에는 Many쪽의 주키만 unique로 설정한다. 왜? Join Table에서 One쪽의 주키값은
여러 번 등장할 수 있고, Many쪽의 주키값은 한 번만 등장할 수 있다. 그래야 OneToMany 관계가 되니까.
Item.java
@ManyToOne
@JoinTable(
name="ITEM_USER",
joinColumns=@JoinColumn(name="ITEM_ID"),
inverseJoinColumns=@JoinColumn(name="USER_ID")
)
private User user;
- 양방향으로 설정할 때는 User에 Item의 콜렉션을 추가한다.
- ITEM_USER라는 연관 테이블을 만들고, 그 테이블에 ITEM_ID라는 컬럼과 USER_ID 컬럼을 만든다.
- ITEM_USER 테이블과 Item 테이블과의 연관을 맺을 컬럼은 USER_ID 컬럼이다. 즉 이 컬럼이 Item 테이블에 생긴다.(InverseJoinColumns)
@OneToMany(mappedBy = "user")
private Set<Item> items = new HashSet<Item>();
- 콜렉션의 변화는 무시한다.