'개체타입의 콜렉션'에 해당되는 글 1건

  1. 2010.06.25 개체 타입의 컬렉션
경매시스템에서 사용자는 한개이상의 경매 품목에 한번이상 입찰가능하다. -> 품목과 사용과 관계에는 1-n 관계가 성립.
또한 하나의 물품에 대한 한개 이상의 입찰이 들어올 수 있다. -> 품목과 입찰 사이에서도 1-n 관계가 성립.
Bid는 입찰로  User와 Item에 공유된다. -> User나 Item의 컴포넌트 타입이 아니라 하나의 독립된 개체 타입이 된다.
이런 개체타입간의  1-n관게를 매핑할때도 컬렉션 타입(Set, List, Map, Bag)을 사용.

< one-to-many> 태그를 사용한 1-n 관계 매핑
public class User {
private Set<Bid> bids = new HashSet<Bid>();
...
// getter / setter
}

<set>태그는 동일 <element> 태그 대신에 <one-to-many> 태그를 사용
<class name="User" table="USER">
..
<set name="bids" table="BID" inverse="true" cascade="all">
<key column="USERID" />
<one-to-many class="Bid" />
</set>
</class>
<class name="Bid" table="BID">
...
</class>
inverse 속성은 영속성 제어와 관련된 속성임.

Set 타입과 Bag타입
User클래스에서  Bid클래스로의 단방향 1-n관계인 경우 User객체에서만 Bid객체로 접근 할수 있고
반대로 Bid객체에서 User객체로 접근할수있는 참조는 없다.
public class User {
private Integer id;
private Set<Bid> bids = new HashSet<Bid>();

// getter / setter...
public void addBid(Bid bid) {
bids.add(bid);
}
}
public class Bid {
private Integer id;
...//User에 대한 참조가 없다
//Set타입에 들어가므로 equals() 메소드와 hashCode() 메소드를 구현
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((id == null) ? : 0 : id.hashCode());
return result;
}
@override
public boolean equals(Obejct obj) {
if (this == obj) return true;
if (obj === null) return false;
if (getClass() != obj.getClass()) return false;
final Bid other = (Bid)obj;
if (id == null || other.getId() == nul) return false;
if (!id.equals(other.id)) return false;
retur true
}
}

<class name="User" table="USER">
...
<set name="bids" table="BID" cascade="save-update, delete">
<key column="USERID" />
<one-to-many class="Bid" />
</set>
</class>
<class name="Bid" table="BID" />
...
</class>
User객체 및 User.bids프로퍼티에 저장된 Bid객체를 저장할수 있음
User user = new User();
Bid bid = new Bid();
user.addBid(bid);
session.save(user);
위코드가 실행되면 다음의 세가지 쿼리가 실행됨
insert into USER values()
insert into BID values()
update BID set USERID=? where ID = ?
1.User 객체를 저장
2. cascade 속성의 값이 "save-update" 이므로 연관된 Bid객체를 저장한다.
3. User 매핑에서 <set>태그의 <key> 칼럼값을 수정한다.

Bag타입의 매핑
User.bids필드타입이 collection인 경우 <bag>태그를 사용하여 매핑
<class name="User" table="USER">
...
<bag name="bids" table="BID" cascade="save-update,delete">
<key column="USERID" />
<one-to-many class="Bid" />
</bag>
</class>

<class name="Bid" table="BID">
...
</class>

양방향 매핑 설정
User입장에서 Bid로의 관계가 1-n이기 때문에, 반대로 Bid에서 User로의 관계는 n-1이 된다.
n-1의 경우 <many-to-one>을 사용한다.

양방향 관계를 설정하기 위해, Bid객체에서 User객체로 접근할수있는 프로퍼티를 추가해야함
public class Bid {
private Integer id;
privaet User user;
..
..
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
User.addBid() 메소드에 User객체와 Bid객체를 연관시켜주는 코드를 추가
public class User {
...
public void addBid(Bid bid) {
bids.add(bid);
bid.setUser(this);
}
}

매핑 설정에 Bid에서 User로의 n-1관계 매핑을 추가.
<class name="Bid" table="BID">
<id name="id" column="ID">
<generator class="identity" />
</id>
<many-to-one name="table" column="USERID" class="User" />
</class>
위와 같이 설정후 Session.save()  로 User객체를 실행하면 다음 뤄기ㅏ 실행됨
insert into USER values()
insert into BID (UserID) values(?)
update BID set USERID = ? where ID = ?
User클래스 매핑의 <set>태그에서 지정한 <key>칼럼과 관련된 커뤼가 마지막에 update로 실행되기 때문.
불필요한 마지막 update쿼리를 방지하기 위해서 <set>태그의 inverse속성의 값을  true 로 지정해준다.
<class name="User" table="USER">
<set name="bids" table="BID" cascade="save-update,delete" inverse="true">
<key column="USERID" />
<one-to-many class="Bid" />
</set>
...
</class>
insert속성을 true로 하면 User측에서는 더이상 Bid객체 저장과 관련된 쿼리를 수행하지 않는다.

delete-orphan을 이용한 삭제
User user = (User)session.get(User.class, new Integer(3));
Set<Bid> bids = user.getBids();
Bid some = ........;
bids.remove(some);
Set.remove() 통해 객체를 삭제했으므로, 삭제된 객체와 매핑되는 테이블 데이터도 함께 삭제될거라고 에상할수있다.
하지만 실제로 객체와 매핑되는 테이블 데이터는 삭제되지 않는다.
대신 BID테이블의 USERID필드의 값이 null이 된다.
컬렉션에서 객체가 삭제되었을때, 테이블에서도 함께 삭제될길 원한다면 cascade 속성에 delete-orphan을 추가해야함.
<set name="bids" table="BID" cascade="all, delete-orphan" inverse="true">
<key column="USERID" />
<one-to-many class="Bid" />
</set>
casecade속성에 delete-orphan을 추가하면 Set.remove() 메소드로 객체를 Set에서 제거하면 DELETE 쿼리가 실행되어 테입르에서도 함께 삭제됨

cascade속성에 delete-orphan을 명시했을때 Set에 저장된 객체를 삭제하기 위해 새로운 Set객체를 할당할 경우 에러 발생.
User user = (User)session.get(User.class, 19);
user.setBids(new HashSet<Bid>()); //delete-orphan 인경우 예외가 발생.

cascade 속성에 delete-orphan이 명시되어 있는 경우에는 Set.clear()메소드를 호출하여 객체를 제거함으로 써
테이블 내에서도 관련 객체를 삭제할수 있다.
User user = (User)session.get(User.class, 19);
user.getBids().clear(); //delete-orphan인 경우 clear() 로 전체 삭제

List 타입
Set타입과 마찬가지로 <element>태그나 <composite-element>태그 대신 <one-to-many>태그를 사용함.
BID테이블에 List 인덱스 정보를 위해  POSITION칼럽 추가시, User와 Bid의 관계를 <list>를 사용해 지정할 수 있다.
<class name="User" table="USER">
<id name="id" column="ID">
<generator class="identity" />
</id>
<list name="bids" table="BID" cascade="all, delte-orphan">
<key column="USERID" />
<list-index column="POSITION" />
<one-to-many class="Bid" />
</list>
</class>

<class name="Bid" table="BID">
<id name="ID" column="ID">
<generator class="identity" />
</id>
<many-to-one name="user" column="USERID" class="USER" />
</class>
<set>과 다른점은 <list>태그에 insert="true"를 명시하지 않음.
inverse 속성을 true 로 지정할 경우 List에 새로운 객체를 추가하거나 기존 객체를 삭제할때 BID 테이블의 POSITION 칼럼에 인덱스 값이 오러바르게 반영되지 않기 때문

Map 타입
List타입과 마찬가지 이유로 inverse 속성을 명시하지 않고 매핑을 설정해 주어야 한다.
<class name="User" table="USER">
<id name=-"id" column="ID">
<generator class="identity" />
</id>
<map name="props" table="USER_PROPERTY" cascade="save-update, delete-orphan">
<key column="USERID" />
<map-key column="PROP_KEY" type="string" />
<one-to-many classes="UserProperty" />
</map>
...
</class>

<class name="UserProeprty" table="USER_PROPERTY">
<id name="id" coumn="ID">,,,</id>
<property name="key" column="PROP_KEY" insert="false" update="false" />
<property name="value" />
....
<many-to-one name="user" column="USERID" class="User" />
</class>




저작자 표시
신고
Posted by 영겁회귀

티스토리 툴바