'Hibernate3'에 해당되는 글 3건

  1. 2010.06.25 개체 타입의 컬렉션
  2. 2010.06.14 컴포넌트 타입의 컬렉션
  3. 2010.06.04 참조에 의한 영속성 전이
경매시스템에서 사용자는 한개이상의 경매 품목에 한번이상 입찰가능하다. -> 품목과 사용과 관계에는 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 영겁회귀

LOG_IMAGEFILE 테이블은 LIST타입과 매핑되는 컬렉션 테이블임.
매핑될 칼럼이 3(NAME, SIZEX, SIZEY)이므로 컴포넌트 타입을 사용해야 할것이다.
package pe.hibernate.chap04.model;

public class ImageFile {
private String name;
private int sizeX;
private int sizeY;

public ImageFile() {}

@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((name== null) ? 0 : name.hashCode());
result = PRIME * result + sizeX;
result = PRIME * result + sizeY;
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) resurn false;
if (getClass() != obj.getClass()) return false;
final ImageFile other = (ImageFile) obj;
if (name == null) {
if (other.anme != null) return false;
} else if (!name.equals(other.name)) {
return false;
}
if (sizeX != other.sizeX) return false;
if (sizeY != other.sizeY) return false;
return true;
}
}
모든 컴포넌트 타입의 클래스에서 equals()와 hashCode()를 구현할 필욘없다.
equals()와 hashCode() 는 set타입의 저장될 경우에 구현해야 한다.
public class Log {
private List<ImageFile> imageFiles = new ArrayList<ImageFile>();
...
}
컴포넌트 타입의 컬렉션 매핑을 설정하는 방법은 기본 데이터 타입의 컬렉션 매핑과 비슷.
<element>태그 대신 <composite-element> 태그를 사용.
<list name="imageFiles" table="LOG_IMAGEGFILE">
<key column="LOGID" />
<list-index column="POSITION" base="1" />
<composite-element class="ImageFile">
<property name="name" column="NAME" />
<property name="sizeX" column="sizeX" />
<property name="sizeY" column="sizeY" />
</composite-element>
</list>

sort 및 order-by 속성을 이용한 컴포넌트 타입의 정렬
SortedMap  컬렉션이나 SortedSet컬렉션에 저장될 컴포넌트 타입을 정렬할 때에도 sort 속성을 사용.
sort 속성의 값을 natural로 지정할 경우 컴포넌트 타입 클래스는 Comparable 인터페이스를 구현해주어야 한다.
package pe.hibernate.chap02.model;

public class ImageFile implements Comparable<ImageFile> {
private String name;
....
public int compareTo(ImageFile to) {
return this.getNames().compareTo(to.getName());
}
}
Comparable 인터페이스를 구현하지 않도록 하려면, 직접 구현해서 sort 속성에 명시해 주어야 한다.
 public class ImageFileComparator implements Comparator<ImageFile> {
public int compare(ImageFile 01, ImageFile 02) {
return 01.getName().compareTo(o2.getName());
}
}
order-by 속성을 사용할 경우 DBMS에서 정렬을 수행하므로 컴포넌트 타입 클래스가 Comparable인터페이스를 구현할 필요한 없으며, 또한 Comparator 구현체를 통해 제공할 필요도 없다.

부모 객체 참조하기
<parent>태그를 사용 부모 객체를 참조할수 있도록 함
<component name="korName" class="Name">
<parent name="employee" />
<property name="surname" column="KOR_SURNAME" />
...
</component>
<composite-element>태그를 사용할때에도 <component>태그를 사용할 때처럼 <parent>태그 사용가능
<set name="imageFiles" table="LOG_MANAGER">
<key column="LOGID" />
<composite-element class="ImageFile">
<parent name="log" />
<property name="name" column="NAME" />
<property name="sizeX" column="sizeX" />
<property name="sizeY" column="sizeY" />
</composite-element>
</sex>
저작자 표시
신고
Posted by 영겁회귀
영속성 전이 : 부모-자식 관계를 갖는 연관에서 부모에 속하는 객체를 저장/삭제할 때 연관되어 있는 자식 객체도 하께 저장하거나 삭제에까지 영향을 미치는 것 (transitive persistence)
하이버네이트는 기본적으로 영속성 전이를 처리하지 않는다. 주요 키값을 공유하는 1-1관계에서 <one-to-one> 속성을 다음과 같이 명시함
<class name="Article" table="ARTICLE">
  <id name="id" column="ID">
    <generator class="identity" />
  </id>

  <one-to-one name="detail" class="ArticleDetail" />
</calss>

<class name="ArticleDetail" table="ARTICLE_DETAIL">
  <id name="id" column="ARTICLE_ID">
    <generator class="foreign">
      <param name="property">article</param>
    </generator>
  </id>

  <one-to-one name="article" class="Article" constrained="true" />
</class>
Article 클래스의 매핑에서 <one-to-one> 태그에 cascade 속성을 명시하지 않음.
이 상태에서 Article 객체와 ArticleDetail객체를 연관시킨 후 Session.save()를 실행하게 되면 Article객체만 저장되고 ArticleDetail객체는 저장되지 않는다.
ArticleDetail detail = new ArticleDetail();
article.setDetail(detail);
detail.setArticle(detail);
session.save(article); //article 객체만 저장됨

<one-to-one name="detail" class="ArticleDetail" cascade="save-update, delete" />
casecade 속성을 추가해 Article 객체의 영속성이 ArticleDetail 객체에까지 전이된다.
cascade 값
 속성 값 Session 메소드
설 명
 save-update save(), update(), saveOrUpdate()
 객체가 저장되거나 수정될때 사용
 delete delete()  객체가 삭제될때
 create persist()  객체를 저장할때
 merge merge()  객체의 값을 영속 객체에 복사할때
 lock lock()  객체를 잠금모드로 읽어 올때 
 refresh refresh()  데이타베이스에서 값을 다시 읽어올때
 evict evict()  세션 캐시에서인스턴스를 제거할때 
 replicate replicate()  준영속 객체의 갑슬 데이타베이스에 복사할때 
 delete-orphan 없음  컬렉션 타입에 적용, 컬렉션에서 객체가 삭제될때 적용
 all 없음  delete-orphan을 제외한 모든 속성 적용

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

티스토리 툴바