특징

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

연관 테이블을 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이어야 한다. 주키 없으니까. 모든 속성을 복합키로 사용하기 때문에.