경매시스템에서 사용자는 한개이상의 경매 품목에 한번이상 입찰가능하다. -> 품목과 사용과 관계에는 1-n 관계가 성립.
또한 하나의 물품에 대한 한개 이상의 입찰이 들어올 수 있다. -> 품목과 입찰 사이에서도 1-n 관계가 성립.
Bid는 입찰로 User와 Item에 공유된다. -> User나 Item의 컴포넌트 타입이 아니라 하나의 독립된 개체 타입이 된다.
이런 개체타입간의 1-n관게를 매핑할때도 컬렉션 타입(Set, List, Map, Bag)을 사용.
< one-to-many> 태그를 사용한 1-n 관계 매핑
<set>태그는 동일 <element> 태그 대신에 <one-to-many> 태그를 사용
양방향 매핑 설정
User입장에서 Bid로의 관계가 1-n이기 때문에, 반대로 Bid에서 User로의 관계는 n-1이 된다.
n-1의 경우 <many-to-one>을 사용한다.
양방향 관계를 설정하기 위해, Bid객체에서 User객체로 접근할수있는 프로퍼티를 추가해야함
매핑 설정에 Bid에서 User로의 n-1관계 매핑을 추가.
List 타입
Set타입과 마찬가지로 <element>태그나 <composite-element>태그 대신 <one-to-many>태그를 사용함.
BID테이블에 List 인덱스 정보를 위해 POSITION칼럽 추가시, User와 Bid의 관계를 <list>를 사용해 지정할 수 있다.
또한 하나의 물품에 대한 한개 이상의 입찰이 들어올 수 있다. -> 품목과 입찰 사이에서도 1-n 관계가 성립.
이런 개체타입간의 1-n관게를 매핑할때도 컬렉션 타입(Set, List, Map, Bag)을 사용.
< one-to-many> 태그를 사용한 1-n 관계 매핑
public class User {
private Set<Bid> bids = new HashSet<Bid>();
...
// getter / setter
...
// getter / setter
}
<set>태그는 동일 <element> 태그 대신에 <one-to-many> 태그를 사용
<class name="User" table="USER">
..
<set name="bids" table="BID" inverse="true" cascade="all">
<set name="bids" table="BID" inverse="true" cascade="all">
<key column="USERID" />
<one-to-many class="Bid" />
<one-to-many class="Bid" />
</set>
</class>
<class name="Bid" table="BID">
<class name="Bid" table="BID">
...
</class>
inverse 속성은 영속성 제어와 관련된 속성임.
Set 타입과 Bag타입
User클래스에서 Bid클래스로의 단방향 1-n관계인 경우 User객체에서만 Bid객체로 접근 할수 있고
반대로 Bid객체에서 User객체로 접근할수있는 참조는 없다.
Set 타입과 Bag타입
반대로 Bid객체에서 User객체로 접근할수있는 참조는 없다.
public class User {
private Integer id;
private Set<Bid> bids = new HashSet<Bid>();
// getter / setter...
public void addBid(Bid bid) {
private Set<Bid> bids = new HashSet<Bid>();
// getter / setter...
public void addBid(Bid bid) {
bids.add(bid);
}
}
public class Bid {
public class Bid {
private Integer id;
...//User에 대한 참조가 없다
//Set타입에 들어가므로 equals() 메소드와 hashCode() 메소드를 구현
@Override
public int hashCode() {
...//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;
int result = 1;
result = PRIME * result + ((id == null) ? : 0 : id.hashCode());
return result;
}
@override
public boolean equals(Obejct obj) {
@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
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">
<set name="bids" table="BID" cascade="save-update, delete">
<key column="USERID" />
<one-to-many class="Bid" />
<one-to-many class="Bid" />
</set>
</class>
<class name="Bid" table="BID" />
...
</class>
User객체 및 User.bids프로퍼티에 저장된 Bid객체를 저장할수 있음
2. cascade 속성의 값이 "save-update" 이므로 연관된 Bid객체를 저장한다.
3. User 매핑에서 <set>태그의 <key> 칼럼값을 수정한다.
Bag타입의 매핑
User.bids필드타입이 collection인 경우 <bag>태그를 사용하여 매핑
User user = new User();
Bid bid = new Bid();
user.addBid(bid);
session.save(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 객체를 저장insert into BID values()
update BID set USERID=? where ID = ?
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">
<bag name="bids" table="BID" cascade="save-update,delete">
<key column="USERID" />
<one-to-many class="Bid" />
<one-to-many class="Bid" />
</bag>
</class>
<class name="Bid" table="BID">
<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() {
privaet User user;
..
..
public User getUser() {
return user;
}
public void setUser(User user) {
public void setUser(User user) {
this.user = user;
}
}
User.addBid() 메소드에 User객체와 Bid객체를 연관시켜주는 코드를 추가
public class User {
...
public void addBid(Bid bid) {
public void addBid(Bid bid) {
bids.add(bid);
bid.setUser(this);
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" />
<many-to-one name="table" column="USERID" class="User" />
</class>
위와 같이 설정후 Session.save() 로 User객체를 실행하면 다음 뤄기ㅏ 실행됨
불필요한 마지막 update쿼리를 방지하기 위해서 <set>태그의 inverse속성의 값을 true 로 지정해준다.
insert into USER values()
insert into BID (UserID) values(?)
update BID set USERID = ? where ID = ?
User클래스 매핑의 <set>태그에서 지정한 <key>칼럼과 관련된 커뤼가 마지막에 update로 실행되기 때문.insert into BID (UserID) values(?)
update BID set USERID = ? where ID = ?
불필요한 마지막 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" />
<one-to-many class="Bid" />
</set>
...
</class>
insert속성을 true로 하면 User측에서는 더이상 Bid객체 저장과 관련된 쿼리를 수행하지 않는다.
delete-orphan을 이용한 삭제
하지만 실제로 객체와 매핑되는 테이블 데이터는 삭제되지 않는다.
대신 BID테이블의 USERID필드의 값이 null이 된다.
컬렉션에서 객체가 삭제되었을때, 테이블에서도 함께 삭제될길 원한다면 cascade 속성에 delete-orphan을 추가해야함.
delete-orphan을 이용한 삭제
User user = (User)session.get(User.class, new Integer(3));
Set<Bid> bids = user.getBids();
Bid some = ........;
bids.remove(some);
Set.remove() 통해 객체를 삭제했으므로, 삭제된 객체와 매핑되는 테이블 데이터도 함께 삭제될거라고 에상할수있다.Set<Bid> bids = user.getBids();
Bid some = ........;
bids.remove(some);
하지만 실제로 객체와 매핑되는 테이블 데이터는 삭제되지 않는다.
대신 BID테이블의 USERID필드의 값이 null이 된다.
<set name="bids" table="BID" cascade="all, delete-orphan" inverse="true">
<key column="USERID" />
<one-to-many class="Bid" />
<one-to-many class="Bid" />
</set>
casecade속성에 delete-orphan을 추가하면 Set.remove() 메소드로 객체를 Set에서 제거하면 DELETE 쿼리가 실행되어 테입르에서도 함께 삭제됨
cascade속성에 delete-orphan을 명시했을때 Set에 저장된 객체를 삭제하기 위해 새로운 Set객체를 할당할 경우 에러 발생.
cascade 속성에 delete-orphan이 명시되어 있는 경우에는 Set.clear()메소드를 호출하여 객체를 제거함으로 써
테이블 내에서도 관련 객체를 삭제할수 있다.
cascade속성에 delete-orphan을 명시했을때 Set에 저장된 객체를 삭제하기 위해 새로운 Set객체를 할당할 경우 에러 발생.
User user = (User)session.get(User.class, 19);
user.setBids(new HashSet<Bid>()); //delete-orphan 인경우 예외가 발생.
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() 로 전체 삭제
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">
<list name="bids" table="BID" cascade="all, delte-orphan">
<key column="USERID" />
<list-index column="POSITION" />
<one-to-many class="Bid" />
<list-index column="POSITION" />
<one-to-many class="Bid" />
</list>
</class>
<class name="Bid" table="BID">
<class name="Bid" table="BID">
<id name="ID" column="ID">
<generator class="identity" />
</id>
<many-to-one name="user" column="USERID" class="USER" />
<many-to-one name="user" column="USERID" class="USER" />
</class>
<set>과 다른점은 <list>태그에 insert="true"를 명시하지 않음.
inverse 속성을 true 로 지정할 경우 List에 새로운 객체를 추가하거나 기존 객체를 삭제할때 BID 테이블의 POSITION 칼럼에 인덱스 값이 오러바르게 반영되지 않기 때문
Map 타입
List타입과 마찬가지 이유로 inverse 속성을 명시하지 않고 매핑을 설정해 주어야 한다.
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">
<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-key column="PROP_KEY" type="string" />
<one-to-many classes="UserProperty" />
</map>
...
...
</class>
<class name="UserProeprty" table="USER_PROPERTY">
<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" />
<property name="key" column="PROP_KEY" insert="false" update="false" />
<property name="value" />
....
<many-to-one name="user" column="USERID" class="User" />
</class>