Whiteship's Note


맵 맵핑하기

Hibernate/Chapter 7 : 2008.02.09 10:24


Entity를 참조하는 값

특징

  • Map<Long, BId> 처럼, Bid의 주키값을 맵의 키값으로 가지는 콜렉션으로 맵핑할 수 있다.
  • 애플리케이션에서만 다를 뿐, 테이블은 변한 거 없다. Bid에 ITEM_ID 외례키 컬럼이 생긴다.

맵핑하기

Item.java
@MapKey(name = "id")
@OneToMany(mappedBy="item")
private Map<Long, Bid> bids = new HashMap<Long, Bid>();
Bid.java
@ManyToOne
private Item item;

ternary association

특징

  • 3항 연관을 맵을 사용해서 표현할 수 있다.
  • Category, Item, User의 관계에서 Category가 Item을 키, User을 값으로 가지는 콜렉션을 가지도록 설정할 수 있다. 왜? Category에 속하는 Item은 유일하고, User는 여러 개의 Item을 추가할 수 있으니까.

맵핑하기

Category.java
@ManyToMany
@MapKeyManyToMany(joinColumns = @JoinColumn(name = "ITEM_ID"))
@JoinTable(name = "CATEGORY_ITEM", joinColumns = @JoinColumn(name = "CATEGORY_ID"), inverseJoinColumns = @JoinColumn(name = "USER_ID"))
private Map<Item, User> itemsAndUser = new HashMap<Item, User>();
  • CATEGORY_ITEM 테이블을 만들고, 이 테이블의 주키를 CATEGORY_ID로 설정한다.
  • 맵의 키는 ITEM_ID, 값은 USER_ID로 맵핑된다.
top


Join Table에 컬럼 추가하기

Hibernate/Chapter 7 : 2008.02.09 10:22


특징

  • 연관 테이블에 추가 속성이 필요할 수 있다.

연관 테이블을 Intermediate Entity로 맵핑하기

특징

  • 연관 테이블로 맵핑 될 새로운 테이블을 작성한다.
  • 양방향 네이게이션이 가능하다.(장점)
  • 연관 클래스를 생성하고 제거하는데 관리해야하는 코드가 늘어난다.(단점)
  • Category나 Item을 추가할 때 CategoryItem에 Cascade 옵션을 사용해서 transitive persistence를 사용할 수 있다.(12장에서 다룸)

맵핑하기

CategoryItem.java
@Entity
public class CategoryItem {

@Embeddable
public static class Id implements Serializable {

@Column(name = "CATEGORY_ID")
private Long categoryId;

@Column(name = "ITEM_ID")
private Long itemId;

public Id() {
}

public Id(Long categoryId, Long itemId) {
this.categoryId = categoryId;
this.itemId = itemId;
}

public boolean equals(Object o) {
if (o != null && o instanceof Id) {
Id that = (Id) o;
return this.categoryId.equals(this.categoryId)
&& this.itemId.equals(that.itemId);
} else {
return false;
}
}

public int hashCode() {
return categoryId.hashCode() + itemId.hashCode();
}
}

public CategoryItem() {
}

public CategoryItem(Category category, Item item) {
this.category = category;
this.item = item;

id.categoryId = category.getId();
id.itemId = item.getId();

item.getCategoryItems().add(this);
category.getCategoryItems().add(this);
}

@EmbeddedId
private Id id = new Id();

@Column(name = "ADDED_ON")
private Date dateAdded = new Date();

@ManyToOne
@JoinColumn(name = "ITEM_ID", insertable = false, updatable = false)
private Item item;

@ManyToOne
@JoinColumn(name = "CATEGORY_ID", insertable = false, updatable = false)
private Category category;

//getter, setter
}
Category.java
@OneToMany(mappedBy = "category")
private Set<CategoryItem> categoryItems = new HashSet<CategoryItem>();
  • 생성자에 참조 무결성을 지키기 위한 코드 추가.
  • 이 객체가 연관을 맺고 있는 클래스들의 상태는 Detached 이거나, Persistent 상태여야 한다. Transient 상태에서 Fake Object를 활용해도 되지 않는다.
Error Log
Hibernate: insert into CategoryItem (ADDED_ON, CATEGORY_ID, ITEM_ID) values (?, ?, ?)
2008-02-08 09:51:31,381 WARN [org.hibernate.util.JDBCExceptionReporter] -
<SQL Error: 0, SQLState: null>
2008-02-08 09:51:31,381 WARN [org.hibernate.util.JDBCExceptionReporter] -
<SQL Error: 0, SQLState: null>
2008-02-08 09:51:31,381 ERROR [org.hibernate.util.JDBCExceptionReporter] - <failed batch>
2008-02-08 09:51:31,381 ERROR [org.hibernate.util.JDBCExceptionReporter] - <failed batch>
2008-02-08 09:51:31,381 ERROR [org.hibernate.event.def.AbstractFlushingEventListener] -
<Could not synchronize database state with session>

연관 테이블을 컴포넌트의 콜렉션으로 맵핑하기

특징

  • 라이프사이클 관리를 별도로 하지 않아도 된다. 컴포넌트 콜렉션을 가지고 있는 Entity에 추가하거나 삭제하기만 하면 된다.
  • 양방향 네비게이션을 할 수 없다. 컴포넌트는 공유될 수 없기 때문에, Item에서 CategoryItem으로 이동하지 못한다. 그치만 SQL을 잘 작성해서 가져올 순 있다.

맵핑하기

CategoryItem.java
@Embeddable
public class CategoryItem {

public CategoryItem() {
}

public CategoryItem(Category category, Item item){
this.category = category;
this.item = item;
}

@Parent
private Category category;

@ManyToOne
@JoinColumn(name = "ITEM_ID", insertable = false, updatable = false)
private Item item;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "ADDED_ON", nullable = false)
private Date dateAdded = new Date();

// getter, setter, equals, hashcode
}
Category.java
@CollectionOfElements
@JoinTable(name = "CATEGORY_ITEM", joinColumns = @JoinColumn(name = "CATEGORY_ID"))
private Set<CategoryItem> categoryItems = new HashSet<CategoryItem>();
  • 양방향 관계를 맵핑할 수 없다. Item에서 CategoryItems 콜렉션을 가질 수 없다. 오직 하나의 Entity에 포함된다. 공유 자원이 아니니까.
  • CategoryItem에 있는 모든 속성은 Not Null이어야 한다. 주키 없으니까. 모든 속성을 복합키로 사용하기 때문에.
top


ManyToMany 관계 맵핑

Hibernate/Chapter 7 : 2008.02.09 10:20


단방향 다대다 관계

  • 대부분 추가적인 정보가 필요하기 때문에, 별도의 assiation class를 만들게 된다. 여기서는 간단한 다대다 맵핑.

Set 타입으로 맵핑하기

Category.java
@ManyToMany
@JoinTable(name = "CATEGORY_ITEM", joinColumns = @JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = @JoinColumn(name = "ITEM_ID"))
private Set<Item> items = new HashSet<Item>();
  • 이 때는 Category 클래스에 별도의 컬럼이 생기지 않는다. @ManyToOne에서 Join Table을 만들었을 때는 inverseJoinColumns의 컬럼이 생겼었다.
  • 콜렉션에 추가할 때(category.getItems().add(itme)) 다음의 SQL 날린다. insert into CATEGORY_ITEM (CATEGORY_ID, ITEM_ID) values (?, ?)

Idbag 타입으로 맵핑하기

Category.java
@ManyToMany
@CollectionId(
columns=@Column(name="CATEGORY_ITEM_ID"),
generator = "sequence",
type = @Type(type="long")
)
@JoinTable(name = "CATEGORY_ITEM", joinColumns = @JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = @JoinColumn(name = "ITEM_ID"))
private Set<Item> items = new HashSet<Item>();
  • surrogate 키를 가지고 있기 때문데, 중복을 허용한다.
  • @CollectionId, @Type은 하이버네이트의 애노테이션이다.
  • 콜렉션에 추가할 때 다음의 SQL 날린다. insert into CATEGORY_ITEM (CATEGORY_ID, CATEGORY_ITEM_ID, ITEM_ID) values (?, ?, ?)

List 타입으로 맵핑하기

Category.java
@ManyToMany
@JoinTable(name = "CATEGORY_ITEM", joinColumns = @JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = @JoinColumn(name = "ITEM_ID"))
@IndexColumn(name = "DISPLAY_POSITION")
private List<Item> items = new ArrayList<Item>();
  • 콜렉션에 추가할 때 다음의 SQL 날린다. insert into CATEGORY_ITEM (CATEGORY_ID, DISPLAY_POSITION, ITEM_ID) values (?, ?, ?)

양방향 다대다 관계

Item.java
@ManyToMany(mappedBy="items")
private Set<Category> categories = new HashSet<Category>();
  • 양방향 관계에서 1쪽의 inverse로 맵핑했었던 이유? 외례키 컬럼을 두 번이나 사용했으니까.
  • 이번에도 마찬가지로, 두 쪽 중에 한 쪽은 inverse로 설정해 주어야 한다.
  • cascade 설정 중에 all, delete, delete-orphans는 다대다 관계에서 의미가 없다. 왜? Value 타입이면 모를까, Entity 타입인데, 다른 Entity가 지워진다고 해서, 해당 Entity까지 지워지라는 보장은 없다. 거의 그런 경우는 없을 것이다. 따라서 의미가 없다. save or update만 사용하는게 좋겠다.
  • 양방향 관계에 있는 콜렉션 타입 결정은 inverse와 관련이 있다. inverse쪽에는 bag과 set처럼 id가 없는 것들만 올 수 있다. 왜? index나 키를 생성하는 쪽 SQL을 무시해버리면 indexColumn에 필요한 정보가 들어갈리 없으니까. 결론적으로, 양쪽 모두 index나 키를 가지는 콜렉션끼리 다대다 관계를 가질 수 없다. 왜? 양쪽 중 한 쪽은 inverse로 설정할 텐데 그러면 그 쪽에 필요한 정보가 생성되지 않을테니까.

'Hibernate > Chapter 7' 카테고리의 다른 글

다형적인 콜렉션 연관  (0) 2008.02.20
다형적인 ManyToOne 연관  (0) 2008.02.20
하이버네이트 애노테이션과 XML 설정 사이의 Fetching 기본값 차이  (0) 2008.02.20
맵 맵핑하기  (0) 2008.02.09
Join Table에 컬럼 추가하기  (0) 2008.02.09
ManyToMany 관계 맵핑  (7) 2008.02.09
OneToMany 관계 맵핑  (1) 2008.02.09
Multy-valued entity 연관  (0) 2008.02.09
OneToOne Join Table 맵핑  (0) 2008.02.09
OneToOne 외례키 맵핑  (0) 2008.02.09
주키를 공유하는 맵핑  (0) 2008.02.09
top


Multy-valued entity 연관

Hibernate/Chapter 7 : 2008.02.09 10:16


  • 1대다 관계
  • 다대다 관계
  • Join Table에 컬럼 추가하기
  • 맵 맵핑하기

'Hibernate > Chapter 7' 카테고리의 다른 글

다형적인 ManyToOne 연관  (0) 2008.02.20
하이버네이트 애노테이션과 XML 설정 사이의 Fetching 기본값 차이  (0) 2008.02.20
맵 맵핑하기  (0) 2008.02.09
Join Table에 컬럼 추가하기  (0) 2008.02.09
ManyToMany 관계 맵핑  (7) 2008.02.09
OneToMany 관계 맵핑  (1) 2008.02.09
Multy-valued entity 연관  (0) 2008.02.09
OneToOne Join Table 맵핑  (0) 2008.02.09
OneToOne 외례키 맵핑  (0) 2008.02.09
주키를 공유하는 맵핑  (0) 2008.02.09
Single-valued entity 연관  (0) 2008.02.09
top