Whiteship's Note


다형적인 ManyToOne 연관

Hibernate/Chapter 7 : 2008.02.20 18:28


특징

  • 다형적인 연관 관계는 Table per concrete class with unions, Table per class per hierachy, Table per subclass 모두 사용할 수 있다.
  • lazy=true로 설정했다면, 하위 타입으로 바로 캐스팅 할 수 없다. Proxy 객체를 가져왔기 때문이다.(XML 설정)
  • session.load()를 사용해서 해당 타입으로 다시 로딩하여 사용하거나, eager fetch query를 사용한다.
Proxy Narrowing
User user = (User) session.get(User.class, userId);
BillingDetails bd = user.getDefaultBillingDetails();
// Narrow the proxy to the subclass, doesn't hit the database
CreditCard cc =
(CreditCard) session.load( CreditCard.class, bd.getId() );
expiryDate = cc.getExpiryDate();
Eager Fetch Query
User user = (User)session.createCriteria(User.class)
.add(Restrictions.eq("id", uid) )
.setFetchMode("defaultBillingDetails", FetchMode.JOIN)
.uniqueResult();
// The users defaultBillingDetails have been fetched eagerly
CreditCard cc = (CreditCard) user.getDefaultBillingDetails();
expiryDate = cc.getExpiryDate();
  • OneToOne 연관도 이것과 같다.
  • 애노테이션을 사용한 맵핑에서는 기본 FetchMode가 EAGER다. 따라서 불필요한 쿼리가 생길 수도 있다.
top


하이버네이트 애노테이션과 XML 설정 사이의 Fetching 기본값 차이

Hibernate/Chapter 7 : 2008.02.20 16:28


하이버 3.0부터는 lazy=true"가 기본값이라고 나와있습니다. (여기에.. Association fetching strategies 참조)

콜렉션이든 단일 클래스든 모두 Lazy Loading.

하지만 이 말은 XML 설정에 국한 된 말입니다. 애노테이션 설정에서는 그렇치 않습니다.
여기에 2.2.5.5. Association fetching 이 부분을 참조하면 다음과 같이 나와있습니다.

@OneToMany와 @ManyToMany 의 FetchMode는 Lazy
@OneToOne과 @ManyToOne의 FetchMode는 Eager

따라서, XML 설정으로 lazy="true"를 생각하고 코딩한 것을 애노테이션으로 옮길 때는 모두 명시적으로 fetch=FetchMode.LAZY로 설정해주어야 예상치 못한 쿼리가 발생하는 것을 방지할 수 있습니다.

예상치 못한 쿼리는 다음과 같이 발생합니다.
사용자 삽입 이미지

이런 상황(상속 구조는 Table Per Subclass로 맵핑)에서.. User가 가지고 있는 DefaultBillingDetails를 가져온다고 가정합니다. User와 DefaultBillingDetails는 @OneToObe 이나 @ManyToOne으로 맵핑할 수 있습니다. 그러면 default가 EAGER죠.

        User loadedUser = (User) session.get(User.class, 1l);
        assertNotNull(loadedUser);
        BillingDetails billingDetails = loadedUser.getDefaultBillingDetails();
        System.out.println("읽어올 때 타입을 알 수 있나? " + new Boolean(billingDetails instanceof CreditCard).toString());

- default 상황에서 발생하는 쿼리

    select
        user0_.id as id3_2_,
        user0_.DEFAULT_BILLING_DETAILS_ID as DEFAULT3_3_2_,
        user0_.name as name3_2_,
        billingdet1_.BILLING_DETAILS_ID as BILLING1_0_0_,
        billingdet1_.USER_ID as USER2_0_0_,
        billingdet1_1_.NUMBER as NUMBER1_0_,
        billingdet1_2_.ACCOUNT as ACCOUNT2_0_,
        case
            when billingdet1_1_.BILLING_DETAILS_ID is not null then 1
            when billingdet1_2_.BILLING_DETAILS_ID is not null then 2
            when billingdet1_.BILLING_DETAILS_ID is not null then 0
        end as clazz_0_,
        user2_.id as id3_1_,
        user2_.DEFAULT_BILLING_DETAILS_ID as DEFAULT3_3_1_,
        user2_.name as name3_1_
    from
        User user0_
    left outer join
        BILLING_DETAILS billingdet1_
            on user0_.DEFAULT_BILLING_DETAILS_ID=billingdet1_.BILLING_DETAILS_ID
    left outer join
        CREDIT_CARD billingdet1_1_
            on billingdet1_.BILLING_DETAILS_ID=billingdet1_1_.BILLING_DETAILS_ID
    left outer join
        BANK_ACCOUNT billingdet1_2_
            on billingdet1_.BILLING_DETAILS_ID=billingdet1_2_.BILLING_DETAILS_ID
    left outer join
        User user2_
            on billingdet1_.USER_ID=user2_.id
    where
        user0_.id=?

User와 User가 가지고 있는 BillingDetails를 left outter join으로 가져옵니다.
BillingDetails를 Table Per Subclass로 맵핑했기 떄문에 CREDIT_CARD와 BANK_ACCOUNT에 left ountter join을 사용하여 모든 레코드를 가져옵니다.
마지막으로 BillingDetails가 ManyToOne으로 참조하는 User를 가져옵니다.(이건 User의 DefaultBillingDetail과 연관을 맺고 있는 User가 아닙니다. 그런 속성은 없습니다. 이 속성은 User가 가지고 있는 BillingDetail 콜렉션과 양방향으로 연관을 맺고 있는 속성입니다.)

굉장한 쿼리가 만들어졌습니다. User가 defaultBillingDetail을 가져와서 아직 사용하지도 않았는데 말이죠.

- @OneToOne 또는 @ManyToOne에서 fetch=FetchMode.LAZY 로 설정한 경우

    select
        user0_.id as id3_0_,
        user0_.DEFAULT_BILLING_DETAILS_ID as DEFAULT3_3_0_,
        user0_.name as name3_0_
    from
        User user0_
    where
        user0_.id=?

딱 User의 정보만 가져왔습니다. 분명 가져오라고 했는데... 쿼리를 날리지 않았습니다. 그러면 이 상태에서 BillingDetail에 있는 pay()라는 메소드를 실행하면 NullPointerException이 떨어질까요?

        User loadedUser = (User) session.get(User.class, 1l);
        assertNotNull(loadedUser);
        BillingDetails billingDetails = loadedUser.getDefaultBillingDetails();
        System.out.println("읽어올 때 타입을 알 수 있나? " + new Boolean(billingDetails instanceof CreditCard).toString());
        billingDetails.pay();
        System.out.println("이제는 읽어올 때 타입을 알 수 있을까? " + new Boolean(billingDetails instanceof CreditCard).toString());

이렇게 해봤습니다.

Hibernate:
    select
        user0_.id as id3_0_,
        user0_.DEFAULT_BILLING_DETAILS_ID as DEFAULT3_3_0_,
        user0_.name as name3_0_
    from
        User user0_
    where
        user0_.id=?
읽어올 때 타입을 알 수 있나? false
Hibernate:
    select
        billingdet0_.BILLING_DETAILS_ID as BILLING1_0_1_,
        billingdet0_.USER_ID as USER2_0_1_,
        billingdet0_1_.NUMBER as NUMBER1_1_,
        billingdet0_2_.ACCOUNT as ACCOUNT2_1_,
        case
            when billingdet0_1_.BILLING_DETAILS_ID is not null then 1
            when billingdet0_2_.BILLING_DETAILS_ID is not null then 2
            when billingdet0_.BILLING_DETAILS_ID is not null then 0
        end as clazz_1_,
        user1_.id as id3_0_,
        user1_.DEFAULT_BILLING_DETAILS_ID as DEFAULT3_3_0_,
        user1_.name as name3_0_
    from
        BILLING_DETAILS billingdet0_
    left outer join
        CREDIT_CARD billingdet0_1_
            on billingdet0_.BILLING_DETAILS_ID=billingdet0_1_.BILLING_DETAILS_ID
    left outer join
        BANK_ACCOUNT billingdet0_2_
            on billingdet0_.BILLING_DETAILS_ID=billingdet0_2_.BILLING_DETAILS_ID
    left outer join
        User user1_
            on billingdet0_.USER_ID=user1_.id
    where
        billingdet0_.BILLING_DETAILS_ID=?

pay() 메소드를 호출하는 순간, BillingDetails 하나를 가져오고 해당 메소드를 호출해 줍니다.
top


Migrate To Hibernate 3.2

Hibernate/migration : 2008.02.14 19:05


API 변경사항

BytecodeProvider extension

  • 하이버네이트의 바이트코드 처리에 필요한 라이브러리를 추가할 수 있는 기능을 추가했다. 이전에는 CGLIB만 사용했는데, 이제는 CGLIB과 Javassist 중에 하나로 선택할 수 있다.

행동 변경사항

Non-transactional access

  • FlushMode.AUTO는 Session내의 변경을 모두 SQL을 실행해서 반영한다. 트랜잭션 범위가 아닐 경우에 auto flush는 생략된다.
  • Second has to do with identifiers generated via an "in-database" strategy (the so-called post-insert id generators). Saves to such entities in previous versions caused an immediate SQL INSERT to be issued in order to determine the generated identifier value. Starting with 3.2, these INSERTS will be delayed when done outside of a transaction.

쿼리 언어 변경사항

Implicit joins are more deterministic

<class name='Currency' lazy='false'>
....
</class>

<class name='Asset'>
<id name='id' ... </id>
<many-to-one name='currency' class='Currency' fetch='select'/>
</class>
  • select a.id, a.currency from Asset a 이런 쿼리는 inner join을 생성한다.
  • select a.id, c from Asset a left join a.currency as c 이렇게 해야 currency가 Null Asset도 전부 가져온다.

Path expressions joining across a collection throw exception

  • select f.bars.baz from Foo f 이런 네비게이션은 이제 에러난다.
  • select b.baz from Foo f join f.bars b 이렇게 써야 한다.

Changed aggregation (count, sum, avg) function return types

  • 스펙에 맡게 리턴타입이 바꼈다.
  • COUNT는 Long을 반환한다.
  • MAX와 MIN은 적용된 필드 타입을 반환한다.
  • AVG는 Double을 반환한다.
  • SUM은 정수타입이 경우에는 Long을 반환한다. 실수일 경우에는 Double, BidInteger일 경우에는 BidInteger, BigDecimal일 경우에는 BidDecimal.
  • 이전 타입을 쓰려면 다음의 코드를 추가한다.
Configuration classicCfg = new Configuration(); 
classicCfg.addSqlFunction( "count", new ClassicCountFunction());
classicCfg.addSqlFunction( "avg", new ClassicAvgFunction());
classicCfg.addSqlFunction( "sum", new ClassicSumFunction());
SessionFactory classicSf = classicCfg.buildSessionFactory();

Improved parameter type guessing

  • 이전에는 파라미터의 타입을 넘어온 값으로 판단했는데, 이제는 쿼리르 보고 판단한다.

Expanded component support

  • First is the ability to bind complete components as parameter values.
Name name = new Name();
name.setFirst( "John" );
name.setLast( "Doe" );
List johnDoes = session.createQuery( "from Person where name = :name" )
.setParameter( "name", name )
.list();
  • ANSI SQL 처럼 "row balue contructor"를 사용할 수 있다.
List johnDoes = session.createQuery( "from Person where name = ('John', 'Doe')" ).list();

Improved boolean literal and parameter handling

  • List pregs = session.createQuery( "from Animal where pregnant = true" ).list(); 이런 쿼리에서의 true 역시 쿼리를 분석하여 SQL을 만들어 낸다.

Native SQL 쿼리 변경사항

Sequence of return values from native sql queries (createSQLQuery() and getNamedQuery())

  • 이전에는 scalar 먼저, entity는 그 다음 순으로 반환했다.
  • 3.2에서 그 순서는 맵핑이나 코드에 있는 것으르 따른다.
  • 따라서 다음의 코드는 에러가 나지 않지만, 만약에 Entity를 먼저 add했으면 에러가 날 것이다.
List result = s.createSQLQuery("select o.*, o.value as anumber from ORDER as o").addScalar("anumber").addEntity(Order.class).list();
Object[] row = (Object[])result.get(0);
Integer number = (Integer)row[0];
Order order = (Order)row[1];

Stored procedures no longer require OUT parameter

  • 이전에는 영향을 받는 row의 수를 반환하는 out 파라미터를 필요로 했었는데 이젠 필수사항이 아니다.
  • check 속성에 none, count, param 을 사용할 수 있다.

'Hibernate > migration' 카테고리의 다른 글

Migrate To Hibernate 3.2  (0) 2008.02.14
Migrate To Hibernate 3.1  (0) 2008.02.14
Migrate To Hibernate 3.0  (2) 2008.02.14
top


Migrate To Hibernate 3.1

Hibernate/migration : 2008.02.14 19:03


API 변경사항

NamingStrategy 인터페이스로 업데이트

  • 인터페이스에 추가된 새로운 메소드를 구현하거나, DefaultNamingStrategy 또는 ImprovedNamingStrategy 클래스를 상속받아라.

이벤트 리스너

  • 이벤트 하나에 여러 개의 리스너를 등록할 수 있다. 이벤트 리스너의 API들이 리스너 배열을 받도록 수정되었다.

설정 변경사항

JDBC 커넥션 release mode

  • 이전(2.0, 3.0)까지는 기본값이 ON_CLOSE였는데, 기본값이 이제(3.1)는 auto로 바꼈다.
  • session.connection()을 호출했을 때는 close()를 호출해야한다. 이 뒤에 배포에서 이 부분은 변경 될 것이다.

이벤트 리스너

  • 이벤트에 여러 개의 리스너를 등록할 수 있다.
  • hibernate.cfg.xml에서 <listener> 대신에 좀 더 정확한 <event> 엘리먼트를 사용한다.

쿼리 언어 변경사항

Stricter checking of invalid queries

  • from Entity e where e.collection.property 이런 쿼리는 작성할 수 없다. 명시적으로 join을 사용해야 한다.
  • fetch를 사용할 때, 주가 되는 쪽을 가져오지 않는 쿼리는 의미가 없으므로 예외를 발생시킨다. ex) select b from A join fetch a.bees b

hbm2java

  • 별도의 툴이 아니라, tools.hibernate.org에 통합됐다. 이 과정에서 깨지거나 유지보수 하지 않는 기능은 제거했다. Hbm2javaCompability을 참조하라.

'Hibernate > migration' 카테고리의 다른 글

Migrate To Hibernate 3.2  (0) 2008.02.14
Migrate To Hibernate 3.1  (0) 2008.02.14
Migrate To Hibernate 3.0  (2) 2008.02.14
top


Migrate To Hibernate 3.0

Hibernate/migration : 2008.02.14 19:02


참조: Hibernate Migration Reference

API 변경사항

패키지 이름

  • net.sf.hibernate에서 org.hibernate로 변경.
  • net.sf.hibernate.expression에서 org.hibernate.criterion으로 변경.
  • 하이버네이트를 참조하는 외부 라이브러러(EHCache 같은 경우) net.sf.ehcache.hibernate.Provider에서 org.hibernate.cache.EhCacheProvider로 변경.

Deprected Interfaces

  • org.hibernate.classic 패키지로 이동됐다.
  • Session 인터페이스의 find(), iterate(), filter(), delete() 대신에 createQuery() 사용해야 된다.
  • Session 인터페이스의 saveOrUpdateCopy()대신에 delete, merge() 해야된다.
  • 배열을 인자로 받는 createSQLQuery()는 deperecated됐다.
  • Lifecycle과 Validatable 인터페이스는 deprecated됐다. 그 대신 Interceptor나 Hibernate 3 이벤트 프레임워크를 사용하라.

종속성

  • lib/README.txt 참조할 것.

예외 모델

  • HibernateExcpetion을 비롯해 모든 예외는 Uncheched Exception으로 바꼈다.

Interceptor 인터페이스

  • 두 개의 새로운 메소드 추가 됐다. 따라서 이 인터페이스 구현체들은 비어있는 메소드 두 개를 구현해야 한다.
  • instantiate()의 인자로 Class 객체 대신에 Entity 이름을 받는 String 값을 받도록 바꼈다.
  • isUnsaved())에서 isTransient()로 메소드 이름이 바꼈다.

UserType, CompositeUserType

  • org.hibernate.usertype 패키지로 이동됐고, 새로운 메소드 몇 개가 추가 됐다.
  • ParameterizedType 인터페이스 추가됐다.

FetchMode

  • FetchMode.Lazy와 FetchMode.EAGER는 deprecated됐다. 좀 더 정확한 이름으로 FetchMode.SELECT와 FetchMode.JOIN으로 바꼈다.

PersistentEnum

  • PersustentEnum 클래스는 Hibernate3에서 제거됐다. UserType을 사용하라.

Blob과 Clob 지원

  • Blob이나 Clob 타입을 detached, serialized, merge() 메소드에 넘길 수 있고, 특정 밴더의 타입으로 다음과 같이 변환할 수도 있다. getWrappedClob(), getWrappedBlob() 사용한다.
clob = (oracle.sql.CLOB) ( (org.hibernate.lob.SerializableClob) foo.getText() ).getWrappedClob();

확장 API

  • org.hibernate.criterion, org.hibernate.mapping, org.hibernate.persister 그리고 org.hibernate.collection 패키지는 상당히 많이 리팩터링 했다.

Metadata 변경사항

Association Fetching 전략

  • lazy="true"를 기본값으로 바꿨다. 따라서 이 설정을 하지 않은 클래스와 컬렉션에 모두 lazy="false"를 붙여야 한다.
  • outer-join 속성이 deprecated됐다. outer-join="true" 대신에 fetch="join", outer-join="false" 대신에 fetch="select"를 사용하라.

식별자 맵핑

  • unsaved-value="0"을 기본값으로 사용한다.
  • 하이버네이트 3에서 natural key(assigned identifier 또는 복합키)를 사용하거나 detached 객체를 쓸 때, 더이상 Interceptor.isUnsaved() 메소드를 구현할 필요가 없다. 힌트 없으면 DB에 쿼리 날려서 객체가 새로 만든 것인지 detached 인지 알아낸다. 따라서 그냥 isUnsaved() 사용하는게 성능에 좋겠다.

콜렉션 맵핑

  • <index>는 준-deprecated 됐다. <list-index>와 <map-key>를 권장한다. <key-many-to-many> 대신에 <map-key-many-to-many>, <composite-index> 대신에 <composite-map-key>를 사용하라.

DTD

Query Language Changes

  • 하이버네이트 3은 ANTLR-based HQL/SQL query translator를 사용한다. 2.1의 쿼리 파서도 사용할 수 있다.
  • 예전 쿼리 파서를 사용하려면 hibernate.query.factory_class 속성에 org.hibernate.hql.classic.ClassicQueryTranslatorFactory
  • 새로운 파서를 사용하려면, org.hibernate.hql.ast.ASTQueryTranslatorFactory를 설정한다.
  • Note: there is a known bug affecting dialects with theta-style outer joins (eg. OracleDialect for Oracle 8i, TimesTen dialect, Sybase11Dialect). Try to use a dialect which supports ANSI-style joins (eg. Oracle9Dialect), or fall back to the old query parser if you experience problems.
  • elements() 대신에 명시적인 join을 사용하라.

설정 변경

  • BEA Weblogic issues 패스.

'Hibernate > migration' 카테고리의 다른 글

Migrate To Hibernate 3.2  (0) 2008.02.14
Migrate To Hibernate 3.1  (0) 2008.02.14
Migrate To Hibernate 3.0  (2) 2008.02.14
top


맵 맵핑하기

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


OneToMany 관계 맵핑

Hibernate/Chapter 7 : 2008.02.09 10:18


Bag으로 맵핑하기

  • Bag은 1대다 양방향 관계를 맵핑하는 가장 효율적인 콜렉션이다. 왜? index도 없고, 중복도 허용하기 때문에, 그냥 넣기만 하면 된다.
  • 그러나 2개의 bag 타입 콜렉션을 동시에 eaget-fetching 할 수 없다.(13장에서 다룬다.)
  • Bag 타입을 사용하려면 변수의 타입은 Collection, 구현체는 ArrayList를 사용한다.
  • 중복 데이터를 넣는 일은 코딩을 잘 해서 방지하면 되지만, 어차피 mappedby 속성(inverse)를 Collection에 걸어두었기 때문에, 여러번 추가해도 SQL은 실행되지 않는다.(그래도.. 메모리 상에서는 콜렉션에 들어갈텐데, 이러면, 안 되지 않을까.. 흠)
Item.java
@OneToMany(mappedBy = "item")
private Collection<Bid> bids = new ArrayList<Bid>();
Bidjava
@ManyToOne
private Item item;

List로 맵핑하기

  • 콜렉션에 있는 Entity들의 index가 필요하다면, List로 맵핑하면 된다.
  • 콜렉션을 맵핑할 때, 콜렉션의 엔티티는 index를 가지고 있을 추가적인 컬럼을 설정한다.(@IndexColumn)
  • 양방향 설정할 때, 콜렉션에 index가 있는 경우(List, Map, 배열)에는 One쪽을 무시(inverse)한다.
  • 왜? 콜렉션이 DB를 수정할 때 필요로 하는 정보(index)를 가지고 있기 때문에, 콜렉션을 기준으로 DB를 수정해야 한다. -> Bid테이블에 Bid_Index 컬럼이 있는데, 이 안에 넣을 값은 Item에 있는 List<Bid> 콜렉션에 넣어야 알 수 있자나. 그러니까 Bid를 기준으로 SQL을 만들 수 없지. 콜렉션에 넣을 때(item.getBids().add(bid)) Bid_Index에 넣을 값을 알 수 있으니까, 콜렉션을 기준으로 SQL을 생성하고, 그 반대쪽 SQL은 발생하지 않도록 inverse 해줘야 해.
  • 따라서 Item과 Bid의 관계에서 Bid쪽의 변화를 무시해야 하는데, @ManyToOne에는 mappedBy 속성이 없다. 그래서 꼼수가 필요하다.
Item.java
@OneToMany
@JoinColumn(name="ITEM_ID", nullable = false)
@IndexColumn(name = "BID_POSITION")
private List<Bid> bids = new ArrayList<Bid>();
Bid.java
@ManyToOne
@JoinColumn(name="ITEM_ID", updatable=false, insertable=false)
private Item item;
  • item.getBids().add(bid); 이렇게 할 때 SQL(update Bid set ITEM_ID=?, BID_POSITION=? where id=?)을 날린다.
  • Bid 와 Item에 설정한 JoinColumn은 같은 이름이어야 한다. 둘 다 Many쪽(여기서는 Bid)에 있는 외례키 컬럼을 나타낸다.
  • JPA는 indexed list를 지원하지 않기 때문에, 하이버네이트의 @IndexColumn 애노테이션을 사용한다.

Join Table로 맵핑하기

  • OneToMany관계가 optional인 경우에 사용하면 좋다. 그러지 않고, 외례키 컬럼이 Null을 허용하도록 해도 되긴 되지만 비추한다.
  • OneToOne관계를 Join Table로 맵핑한 것과 비슷하지만, 그 때는 두 개의 키를 모두 unique로 설정했지만, 이번에는 Many쪽의 주키만 unique로 설정한다. 왜? Join Table에서 One쪽의 주키값은 여러 번 등장할 수 있고, Many쪽의 주키값은 한 번만 등장할 수 있다. 그래야 OneToMany 관계가 되니까.
Item.java
@ManyToOne
@JoinTable(
name="ITEM_USER",
joinColumns=@JoinColumn(name="ITEM_ID"),
inverseJoinColumns=@JoinColumn(name="USER_ID")
)
private User user;
  • 양방향으로 설정할 때는 User에 Item의 콜렉션을 추가한다.
  • ITEM_USER라는 연관 테이블을 만들고, 그 테이블에 ITEM_ID라는 컬럼과 USER_ID 컬럼을 만든다.
  • ITEM_USER 테이블과 Item 테이블과의 연관을 맺을 컬럼은 USER_ID 컬럼이다. 즉 이 컬럼이 Item 테이블에 생긴다.(InverseJoinColumns)
@OneToMany(mappedBy = "user")
private Set<Item> items = new HashSet<Item>();
  • 콜렉션의 변화는 무시한다.

'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


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


OneToOne Join Table 맵핑

Hibernate/Chapter 7 : 2008.02.09 10:15


특징

  • 만약 One To One 관계에 있는 두 Entity가 optional 하다면...
  • 이전에 배운 외례키 사용하는 맵핑을 한 다음에, 외례키를 nullable하게 설정하게 할 수 있겠지만...
  • 관계에 추가적인 속성이 추가될 수도 있을테니, 두 Entity의 id를 모두 unique하게 가지고 있고, Join Table을 만들 수도 있다. ORM이 이런 작업을 해줄 수 있다.
  • CaveatEmptor에서는 Shipment와 Item의 관계. Shipment가 Item을 가지고 있을 수도 있고(물건을 배송 했다.), 없을 수도(배송을 안해서 물건 판 사람이 돈을 못 받을 것이다.) 있다. 배송을 했을 경우에만 두 관계를 나타내는 테이블에 레코드를 추가해야 할 것이다.

맵핑하기

Shipment.java
@OneToOne
@JoinTable(name = "ITEM_SHIPMENT", joinColumns = @JoinColumn(name = "SHIPMENT_ID"), inverseJoinColumns = @JoinColumn(name = "ITEM_ID"))
private Item auction;
  • Item과 Shipment는 별개의 라이프 사이클을 가지고 있다가, Shipment에 Item을 setting하는 순간, ITEM_SHIPMENT에 레코드가 추가된다.
  • 만약 Join Table에 별도의 속성이 더 필요할 때가 있는데, 그럴 때는 2차 테이블(8장에서 살펴볼 예정)을 사용해서 설정할 수 있다.
@Entity
@Table(name = "SHIPMENT")
@SecondaryTable(name = "SHIPMENT_ITEM")
public class Shipment {

...

@OneToOne
@JoinColumn(table = "SHIPMENT_ITEM", name = "ITEM_ID")
private Item auction;

...
}
  • SecondTable을 정의해 두고, 그 테이블로 옮길 속성을 JoinColumn에서 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


OneToOne 외례키 맵핑

Hibernate/Chapter 7 : 2008.02.09 10:12


특징

  • 주키를 공유하는 것이 아니라, 외례키로 관계를 맺는다.
  • User---->Address 의 관계에서 User는 Source, Address는 Target.
  • Source에서 Target이 한 개씩 맵핑되기 때문에, ManyToOne으로 Unique 속성을 true로 맵핑한다.(XML에서만)
  • 양방향으로 맵핑할 때는 Address---->User로 맵핑도 해줘야 한다.
  • 하이버네이트에 Address의 user가 반대쪽 관계를 나타내는 속성의 inverse라는 것을 표현해준다.

맵핑하기

  • 단뱡향 맵핑
User.java
@OneToOne
@JoinColumn(name="SHIPPING_ADDRESS_ID")
@Cascade( { CascadeType.SAVE_UPDATE })
private Address shippingAddress;
  • Address의 주키 속성은 그냥 일반적인 id로 설정하고 기본 주키 생성기를 사용한다. 커스텀 주키 생성기 사용할 필요 없슴.
  • 양방향 맵핑
Address.java
@OneToOne(mappedBy = "shippingAddress")
private User user;
  • mappedBy를 설정해 주어야 Address에 별도의 외례키 컬럼을 만들지 않는다. 왜? inverse라는 걸 알려줬으니까. inverse? 이건 이미 이 반대 방향의 관계에서 이 둘의 관계에 필요한 속성이 있으니까 불필요한 컬럼(또는 추가적인 SQL)을 만들지 않도록 하이버한테 알려주는 거지.

'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


주키를 공유하는 맵핑

Hibernate/Chapter 7 : 2008.02.09 10:11


특징

  • 두 테이블에서 같은 주키값을 사용한다.
  • 문제는 A-->B 에서 B가 A의 주키값을 사용하도록 하는 것이다. 주기를 공유하도록 설정해야 한다.

맵핑하기

  • JPA는 공유할 주키 생성기를 지원하는 표준화된 방법이 없다.
  • 하이버네이트는 커스텀 id 생성기를 위한 확장 애노테이션을 제공한다.
User.java
@OneToOne
@PrimaryKeyJoinColumn
@Cascade( { CascadeType.SAVE_UPDATE })
private Address shippingAddress;
  • @PrimaryKeyJoinColumn: JOINE을 이용한 상속구조(Table Per Subclass)에서 하위 타입에서 상위 타입의 주키를 자신의 주키이자 외례키로 설정할 때 사용. 여기(1:1맵핑)서는 참조하는 쪽(User)의 주키를 참조되는 쪽(Address)의 외례키로 사용하겠다는 설정이다.
Address.java
@Id
@GeneratedValue(generator = "addressIdGenerator")
@GenericGenerator(name = "addressIdGenerator", strategy = "foreign", parameters = @Parameter(name = "property", value = "user"))
@Column(name = "ADDRESS_ID")
private Long id;

@OneToOne
private User user;
  • JPA에서는 커스텀 식별자 생성기를 만들 수 없기 때문에, 하이버네이트 애노테이션을 이용해야 한다.
테스트 코드
@Test
public void userAndAddress() throws Exception {
Session session = sessionFactory.openSession();
User user = new User();

Address address = new Address();
// Address의 user 속성은 null이면 안 된다. id를 세팅해 주려고 할 때 identifierGeneration 에러 난다.
address.setUser(user);

user.setShippingAddress(address);
session.save(user);

session.flush();
session.close();
assertNotNull(user.getId());
assertNotNull(user.getShippingAddress().getId());
}
  • Fake Object인가... 저렇게 하지 않으면 에러 발생.

'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


Single-valued entity 연관

Hibernate/Chapter 7 : 2008.02.09 10:10


  • User에 Component로 맵핑했던 Address를 별도의 테이블로 구분하고 싶을 수 있다. 다른 Entity에서 Address를 공유할 수 있다. 이럴 때 1:1 맵핑이 된다.
  • 주키를 공유하는 맵핑
  • OneToOne 외례키 맵핑
  • 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


객체 상태 전이하기

Hibernate/Chapter 6 : 2008.02.07 22:22


Parent와 Child

  • Parent Entity와 Child Entity 사이의 관계를 다루는 세 가지 방법
    1. 각각을 별도의 객체로 다룬다: Bid 객체를 매번 save(), delete() 해준다. 물론 객체쪽 콜렉션에서도 이것을 다루기 위한 add()와 remove() 같은 메소드가 필요하다.
    2. Child 를 Value Type으로 만든다: Bid를 @Embeddable로 만들고 Parent의 라이프사이클을 따르도록 한다. 그러나 Bid는 분명 Entity다. Item만 Bid을 가지는게 아니라, User도 똑같은 Bid를 가질 수 있고, Item과 연관을 맺는 successfulIBid의 경우를 봐도, Bid는 분명히 공유되는 것이기 때문에, Entity가 되어야 한다.
    3. Transitive Persistence 기능을 사용한다: 하이버네이트가 연관된 Entity 객체의 라이프사이클을 자동으로 관리하도록 설정하여, 코드 수를 줄일 수 있다.

Transitive Persistence

  • Item을 저장하거나 수정할 때 그와 연관된 Bid 객체를 같이 Persistent 상태로 전이할 수 있다.
  • cascade 속성을 사용하여 설정한다. 두 방향 중 한 방향에만 설정할 수 있다. 그런데 Bid는 나중에 생성되는 객체니까, 거기에 설정해봤자 별 의미가 없다.
	@OneToMany(mappedBy = "item")
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private Set<Bid> bids = new HashSet<Bid>();
  • JPA 애노테이션을 사용해서 cascade 설정하면, Hibernate session에서는 안 먹힌다.

삭제 전이

  • Parent를 삭제할 때 그것과 연관된 모든 Child를 삭제하도록 설정할 수 있다. 이것도 역시 코딩을 줄여준다.
	@OneToMany(mappedBy = "item")
@Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.DELETE })
private Set<Bid> bids = new HashSet<Bid>();
  • for 루프를 돌리면서 Item이 가지고 있는 모든 Bid를 삭제한 뒤에, Item을 지우는 것과 같은 결과가 된다. 역시 코딩을 줄여준다.
  • 문제는 만약에 User가 삭제될 Bid를 가지고 있다면, 그 Bid는 메모리 상에서는 삭제 되지 않을 것이다. 그런데 DB에서는 삭제 된다. 불일치가 생긴다.
  • 그래서 메모리 상에서도 지워주기 위해서, 삭제될 Item이 가지고 있는 Bid를 가지고 있는 User를 찾아서 그 User가 가지고 있는 Bid 목록에서 삭제할 Bid를 remove 해야한다. 다량의 어지러운 코딩이 필요하다.

Orphan 삭제 전이

  • Value Type의 콜렉션에 들어있는 객체 하나를 메모리 상에서 삭제하면, 하이버네이트는 그걸 바로 DB에 반영해서 해당 Row를 지워준다. 왜? Value Type이니까 Parent에 종속적인 라이프사이클을 가지고 있고, 공유되지도 않을테니까.
  • Entity Type의 콜렉션에 들어있는 객체 하나를 지우려면, 별도의 라이프사이클을 가지고 있기 때문에, Parent의 콜렉션에서 지워주는 일은 당연히 해야 되고, session.delelte()를 호출해 주어야 한다. 모든 Parent를 찾아서 지워주는 일도 보통 일이 아니고, 잘못하면 외례키 제약을 깨트릴 수도 있다.
  • Orpahn Delete Cascade 속성은 컬렉션에서 해당 객체를 지울 때, 이제 그 객체를 참조하는 다른 Entity들이 없으니까, 이만 DB에서 지워도 된다는 설정이다.
	@OneToMany(mappedBy = "item")
@Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.DELETE,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
private Set<Bid> bids = new HashSet<Bid>();
  • 이렇게 설정해 두면, 원래는 anItem.getBids().remove(aBid); session.delete(aBid); 이렇게 해야 했는데, anItem.getBids().remove(aBid); 이렇게만 호출해도 DB에서 삭제해 준다.
top


Embedded 객체 맵핑하기

Hibernate/Chapter 6 : 2008.02.07 22:20


Embeddable 맵핑하기

@Embeddable
public class Image {

@Parent
private Item item;

@Column(length = 255, nullable = false)
private String name;

@Column(length = 255, nullable = false)
private String filename;

@Column(nullable = false)
private int sizeX;

@Column(nullable = false)
private int sizeY;
}
  • 모든 속성이 복합키로 사용되기 때문에, 모두 Not Null로 설정해주어야 한다.
  • back point 필요 없으면 @Parent 애노테이션 붙인 속성 없애면 된다.

컬렉션 맵핑하기

	@CollectionOfElements
@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name = "IMAGE_ID"))
@CollectionId(columns = @Column(name = "ITEM_IMAGE_ID"),
type = @Type(type = "long"), generator = "sequence")
@AttributeOverride(name = "element.name",
column = @Column(name = "IMAGENAME", length = 255, nullable = false))
private Set<Image> images = new HashSet<Image>();
  • 컴포넌트의 속성을 재정의 할 수 있다.
  • 컴포넌트 속성이 Null을 포함하게 하려면, @CollectinoId()를 추가해주면 된다.
top


Sorted 콜렉션과 Ordered 콜렉션 맵핑하기

Hibernate/Chapter 6 : 2008.02.07 22:18


Sorted 타입 맵핑하기

  • @Sort 애노테이션을 사용해서, Sorter Set과 Sorted Map 타입으로 맵핑할 수 있다.
  • type 속성에서 Comparator를 사용할 수도 있다.
@CollectionOfElements
@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name = "ITEM_ID"))
@Column(name = "FILENAME")
@Sort(type = SortType.NATURAL)
private SortedSet<String> images = new TreeSet<String>();

Ordered 타입 맵핑하기

  • Map, Set 그리고 Bag을 DB에서 읽어올 때 정렬해서 가져올 수 있다.
  • 하이버 @OrderBy 애노테이션의 clause 속성의 값은 DB에 직접 전달되는 SQL이다.
@CollectionOfElements
@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name = "ITEM_ID"))
@Column(name = "FILENAME")
@OrderBy(clause = "FILENAME asc")
private Set<String> images2 = new HashSet<String>();
top


기본 콜렉션 맵핑하기

Hibernate/Chapter 6 : 2008.02.07 22:17


Set 타입 맵핑하기

  • 외례키를 포함하여 모든 컬럼을 복합키로 사용한다.
@CollectionOfElements(targetElement = java.lang.String.class)
@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name = "ITEM_ID"))
@Column(name = "FILENAME", nullable = false)
private Set<String> images = new HashSet<String>();

List 타입 맵핑하기

  • 컬렉션의 index를 주키로 사용하는 테이블을 정의한다.
  • base 속성으로 시작 index를 정할 수 있다. 기본은 0
@CollectionOfElements
@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name = "ITEM_ID"))
@IndexColumn(name = "POSITION", base = 1)
@Column(name="FILENAME")
private List<String> images2 = new ArrayList<String>();
  • 위 설정에서 @IndexColumn을 빼먹으면, Bag 타입으로 맵핑된다.

Map 타입 맵핑하기

  • 맵의 키가 복합 타입일 때는 해당 타입의 모든 속성을 키로 등록할 수 있다.
  • @MapKey는 JPA가 아니라 하이버 애노테이션이다.
@CollectionOfElements
@JoinTable(name = "ITEM_IMAGE", joinColumns = @JoinColumn(name = "ITEM_ID"))
@MapKey(columns = @Column(name = "IMAGENAME"))
@Column(name = "FILENAME")
private Map<String, String> images3 = new HashMap<String, String>();
top


컴포넌트 콜렉션

Hibernate/Chapter 6 : 2008.02.07 22:16


  • 일반 POJO로 구현하고, equals() 메소드에서 모든 속성을 비교해야 한다. 하이버가 dirty checking 할 때 사용한다.
  • Set으로 맵핑할 때는 컴포넌트의 모든 속성이 복합키로 사용되기 때문에, not-null 속성을 true로 해줘야 한다.
  • Parent 속성을 사용하면 컴포넌트가 자신을 소유한 쪽으로 네비게이션을 할 수 있다.(불완전 양방향 설정)
  • Collection 인터페이스와 ArrayList를 사용하거나, Map을 사용하면, equlas()와 hashmap()을 구현할 필요도 없고, nullable 컬럼을 가질 수도 있다.
top


콜렉션 인터페이스 선택하기

Hibernate/Chapter 6 : 2008.02.07 22:15


특징

  • 꼭 변수의 타입은 구현체가 아닌 인터페이스를 사용해야 한다. private <<Interface>> images = new <<Iimplementataion>>();
  • 콜렉션은 항상 바로 초기화를 하도록 한다.(권장사항)
  • 하이버네이트는 선언된 필드를 초기화 할 때 콜렉션을 감싼다.

콜렉션

  • java.util.Set -> java.util.HashSet: 중복을 허용하지 않고, 순서를 따지지 않는다. 가장 흔하게 사용되는 타입.
  • java.util.SortedSet -> java.util.TreeSet: 정렬 조건을 설정할 수 있다.
  • java.util.List -> java.util.ArrayList: 인덱스를 가지고 있다. 중복 허용한다.
  • java.util.Collection -> java.util.ArrayList: Bag을 맵핑할 때 사용한다. 중복을 허용하고 순서는 없다.
  • java.util.Map -> java.util.HashMap: 키, 값 쌍으로 맵핑되어 있다.
  • java.util.Sortedmap -> java.util.TreeMap: 정렬할 수 있다.
  • 배열: 배열 타입을 감쌀 수는 없다. 배열은 사용하지 않는게 좋겠다.(lazy loading, optimized dirty checking 등 이 밖에도 영속성 콜렉션을 다루는데 있어서 편리하고 성능을 높이는 기능들을 사용할 수 없다.)
top


Value Type의 Set, Bag, List, Map

Hibernate/Chapter 6 : 2008.02.07 22:14


  • Entity가 Value Type 객체 하나를 가지고 있을 때는 Entity를 저장한 테이블의 레코드에 같이 저장된다.
  • Value Type의 콜렉션과 연관을 맺고 있을 때는 별도의 테이블이 필요하다.
  • 콜렉션 인터페이스 선택하기
  • Sorted: 자바 메모리 상에서 정렬이 되는 것. Ordered: 데이터베이스 레벨에서 order by 절을 사용해서 정렬한다.
  • Bag, List는 Sorting을 설정할 수 없다. 인덱스가 있으니까... 대신 Order는 할 수 있다.
  • Set이나 Map을 Soring이 아닌 Order를 사용하고 싶을 때는 order-by 속성을 사용한다.(XML 설정)
top


Hibernate Core VS JPA

Hibernate/Chapter 5 : 2008.02.04 17:39


Hibernate JPA
네 가지 상속 맵핑 전략을 제공하고, 상속 전략을 혼용하는 것이 가능하다. 네 가지 맵핑 전략을 표준화 했다. 이 중에선 Table Per Class Hierarchy와 Table Per Subclass만 JPA 호환 필수요소다.
영속성 대상이 되는 클래스의 상위 클래스는 abstract class나 interface도 될 수 있다. abstract class만 이식 가능하다.
Value Type을 위한 유연한 내장 타입과 컴버터를 제공한다. 표준화된 방법으로 맵핑 타입을 자동으로 찾아 준다. 커스텀 타입을 찾을 때는 하이버네이트 확장 애노테이션을 사용한다.
막강한 타입 확장 시스템 Enum을 위한 내장 타입 표준이 필요하다.

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

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


Enum Type 만들기

Hibernate/Chapter 5 : 2008.02.04 17:20


클래스 구현하기.

  • EnhancedUserType와 ParameterizedType 인터페이스를 구현한다.
private Class<Enum> enumClass;
    
    public void setParameterValues(Properties parameters) {
        String enumClassName =
            parameters.getProperty("enumClassname");
        try {
            enumClass = ReflectHelper.classForName(enumClassName);
        } catch (ClassNotFoundException cnfe) {
            throw new
              HibernateException("Enum class not found", cnfe);
        }
    }

    public Class returnedClass() {
        return enumClass;
    }

    public Object fromXMLString(String xmlValue) {
        return Enum.valueOf(enumClass, xmlValue);
    }

    public String objectToSQLString(Object value) {
        return '\'' + ( (Enum) value ).name() + '\'';
    }

    public String toXMLString(Object value) {
        return ( (Enum) value ).name();
    }

    public Object nullSafeGet(ResultSet rs,
                              String[] names,
                              Object owner)
            throws SQLException {
        String name = rs.getString( names[0] );
        return rs.wasNull() ? null : Enum.valueOf(enumClass, name);
    }

    public void nullSafeSet(PreparedStatement st,
                            Object value,
                            int index)
            throws SQLException {
        if (value == null) {
            st.setNull(index, Hibernate.STRING.sqlType());
        } else {
            st.setString( index, ( (Enum) value ).name() );
        }
    }
  • setParameterValues()는 클래스 이름 가져와서 Class 변수에 세팅한다.
  • fromXMLString(), objectToSQLString(), toXMLString()는 XML과 Enum의 value를 맵핑한다.
  • nullSafeGet()는 DB에서 value를 읽은 다음 Enum으로 반환한다.
  • nullSafeSet()는 Enum에서 DB로 저장할 value를 뽑아낸다.

맵핑하기

  • JPA의 경우 커스텀 타입 클래스 만들지 않고도 String으로 저장하거나, 선택된 Enum의 value를 저장할 수 있다.
public class Comment {
    ...
    @Enumerated(EnumType.STRING)
    @Column(name = "RATING", nullable = false, updatable = false)
    private Rating rating;
    ...
}

쿼리 작성하기

  • 다음과 같이 쿼리를 작성할 수 있다.
Query q =
    session.createQuery(
      "from Comment c where c.rating = auction.model.Rating.BAD"
    );
  • c.rating의 rating은 DB 컬럼의 이름이 아니라 객체의 속성이름이다.
"from Member m where m.memberType = '" + MemberType.ADMIN + "'"
"from Member m where m.memberType = chapter5.customType.example.MemberType.ADMIN"

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

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


ParameterizedType 만들기

Hibernate/Chapter 5 : 2008.02.04 17:20


클래스 구현하기

  • DB에 값을 저장할 때 특정 조건에 따라 다른 값으로 변환하여 저장할 필요가 있다면, 파라미터가 필요하다. 이럴 때 유요하다.
  • ParameterizedType 인터페이스를 구현한다.
private Currency convertTo;

	@Override
	public void setParameterValues(Properties parameters) {
		this.convertTo = Currency.getInstance(
                parameters.getProperty("convertTo"));
	}

맵핑하기

  • 같은 Value Type 클래스 타입의 객체들 여럿을 맵핑 할 때는 TypeDef 애노테이션을 사용해서, 전역에서 사용할 설정을 정의해 둘 수 있다.
@org.hibernate.annotations.TypeDefs({
    @org.hibernate.annotations.TypeDef(
        name="monetary_amount_usd",
        typeClass = persistence.MonetaryAmountConversionType.class,
        parameters = { @Parameter(name="convertTo", value="USD") }
    ),
    @org.hibernate.annotations.TypeDef(
        name="monetary_amount_eur",
        typeClass = persistence.MonetaryAmountConversionType.class,
        parameters = { @Parameter(name="convertTo", value="EUR") }
    )
})
  • 위의 설정은 import문 바로 아래 또는 class정의 부분 위나, 별도의 자바 파일, package-info.java 같은 클래스에 둔다.(2.2.1 참조)
  • 다음과 같이 참조해서 사용할 수 있다.
@org.hibernate.annotations.Type(type = "monetary_amount_eur")
@org.hibernate.annotations.Columns({
  @Column(name = "BID_AMOUNT"),
  @Column(name = "BID_AMOUNT_CUR")
})
private MonetaryAmount bidAmount;

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

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


CompositeUserType 만들기

Hibernate/Chapter 5 : 2008.02.04 17:19


클래스 구현하기

public class MonetaryAmountCompositeUserType implements CompositeUserType {

	public String[] getPropertyNames() {
		return new String[] { "amount", "currency" };
	}

	public Type[] getPropertyTypes() {
		return new Type[] { Hibernate.BIG_DECIMAL, Hibernate.CURRENCY };
	}

	public Object getPropertyValue(Object component, int property)
			throws HibernateException {
		MonetaryAmount monetaryAmount = (MonetaryAmount) component;
		if (property == 0)
			return monetaryAmount.getAmount();
		else
			return monetaryAmount.getCurrency();
	}

	public Object nullSafeGet(ResultSet resultSet, String[] names,
			SessionImplementor session, Object owner)
			throws HibernateException, SQLException {
		BigDecimal value = resultSet.getBigDecimal(names[0]);
		if (resultSet.wasNull())
			return null;
		Currency currency = Currency.getInstance(resultSet.getString(names[1]));
		return new MonetaryAmount(value, currency);
	}

	public void nullSafeSet(PreparedStatement statement, Object value,
			int index, SessionImplementor arg3) throws HibernateException,
			SQLException {
		if (value == null) {
			statement.setNull(index, Hibernate.BIG_DECIMAL.sqlType());
			statement.setNull(index + 1, Hibernate.CURRENCY.sqlType());
		} else {
			MonetaryAmount amount = (MonetaryAmount) value;
			String currencyCode = amount.getCurrency().getCurrencyCode();
			statement.setBigDecimal(index, amount.getAmount());
			statement.setString(index + 1, currencyCode);
		}
	}

	public void setPropertyValue(Object arg0, int arg1, Object arg2)
			throws HibernateException {
		throw new UnsupportedOperationException("Immutable MonetaryAmount!");
	}

}
  • CompositeUserType 인터페이스를 구현한다.
  • nullSafeSet()은 ResultSet에 담겨있는 두 개의 값을 Monetary 객체의 속성값으로 변환하면 된다.
  • nullSafeSet()은 객체가 가진 두 개의 값을 DB에 저장하도록 statement를 수정한다.
  • getPropertyNames()를 사용해서 Value Type의 속성들을 알려준다.
  • getPropertyValue()를 사용해서 Value Type의 각각의 속성이 가진 값을 알려준다.
  • setPropertyValue()를 사용해서 Value Type의 속성에 값을 설정한다.

맵핑하기

@org.hibernate.annotations.Type(
    type = "persistence.MonetaryAmountUserType"
)
@org.hibernate.annotations.Columns(columns = {
    @Column(name="INITIAL_PRICE"),
    @Column(name="INITIAL_PRICE_CURRENCY", length = 2)
})
private MonetaryAmount initialPrice;
  • 다음과 같이 쿼리를 작성할 수 있다.
from Item i
where i.initialPrice.amount > 100.0
  and i.initialPrice.currency = 'AUD'

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

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


UserType만들기

Hibernate/Chapter 5 : 2008.02.04 17:18


클래스 구현하기

public class MonetaryAmountUserType implements UserType {

	public Object assemble(Serializable cached, Object owner)
			throws HibernateException {
		return cached;
	}

	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}

	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) value;
	}

	public boolean equals(Object x, Object y) throws HibernateException {
		if (x == y)
			return true;
		if (x == null || y == null)
			return false;
		return x.equals(y);
	}

	public int hashCode(Object x) throws HibernateException {
		return x.hashCode();
	}

	public boolean isMutable() {
		return false;
	}

	public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
			throws HibernateException, SQLException {

		BigDecimal valueInUSD = resultSet.getBigDecimal(names[0]);
		if (resultSet.wasNull())
			return null;

		// DB에서 가져온 다음에 원하는 값으로 캐스팅 해서 반환.

		return null;
	}

	public void nullSafeSet(PreparedStatement statement, Object value, int index)
			throws HibernateException, SQLException {
		
		// SQL을 DB에 보내기 전에 statement의 특정 위치에 들어갈 값을 변경한다.

	}

	public Object replace(Object original, Object target, Object owner)
			throws HibernateException {
		return original;
	}

	public Class returnedClass() {
		return MonetaryAmount.class;
	}

	public int[] sqlTypes() {
		return new int[] { Hibernate.BIG_DECIMAL.sqlType() };
	}

}
  • sqlTypes() 메소드는 하이버네이트가 어떤 SQL 컬럼 타입으로 DDL 스키마를 생성할 지 알려준다.
  • returnClass() 메소드는 어떤 자바 타입을 맵핑할 것인지 알려준다.
  • deepCopy()는 스냅샷 객체을 전달해 준다. 따라서, immutable한 타입일 경우에는 그냥 넘기면 된다.
  • disassemble()은 MonetaryAmount를 하이버네이트 2차 캐쉬에 집어 넣을 때 호출된다. 이곳에서 직렬화된 상태로 데이터를 캐슁한다.
  • assemble()은 캐쉬된 상태의 데이터를 MonetaryAmount 객체로 변환한다.
  • replace()는 detached 상태의 객체를 merging할 때 사용한다.
  • equals()는 dirty checking 할 때 사용한다.
  • nullSafeGet()은 ResultSet에서 데이터를 가져올 때 사용한다.
  • nullSafeSet()은 PreparedStatement에 저장할 값을 설정할 때 사용한다.

맵핑하기

@org.hibernate.annotations.Type(
    type = " persistence.MonetaryAmountUserType"
)
@Column(name = "INITIAL_PRICE")
private MonetaryAmount initialPrice;

단점

  • 하이버네이트는 Monetary Amount의 속성을 모른다. 따라서 그 안에 있는 amount나 currency를 가져오는 쿼리를 작성할 수 없다.

해결책

  • CompositeUserType을 사용하면 된다.

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

Hibernate Core VS JPA  (0) 2008.02.04
Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
top


맵핑 타입 사용하기

Hibernate/Chapter 5 : 2008.02.04 00:17


  • type 속성을 명시하지 않아도 된다.
  • java.lang.String 타입의 변수는 기본 맵핑으로 string을 사용한다.
  • java.util.Date는 timestamp를 사용한다.
  • JPA를 사용할 때는 @Temporal을 사용해서 정확도를 명시해 준다.
  • 기본값은 TemporalType.TIMESTAMP, (가용한 값은, TIEM, DATE)
  • 하이버네이트 맵핑 타입을 명시적으로 표현하고 싶을 때는 하이버네이트의 @Type 애노테이션 사용.
  • 커스텀 타입도 만들 수 있다.

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

Enum Type 맵핑하기 - 실습  (0) 2008.02.04
Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
top


내장된 맵핑 타입

Hibernate/Chapter 5 : 2008.02.04 00:17


특징

  • ORM은 자바의 타입과 SQL 데이터타입을 중개해 주는 역할을 해야한다.
  • 하이버는 보통 자바 타입 이름을 따르고 있다. 하지만 자바 타입 하나에 여러 하이버네이트 타입이 있을 수 있다.
  • Custom Value Type을 정의할 수 있다.

자바 기본 타입 맵핑

  • 벤더가 ANSI 표준을 따르지 않을 수도 있다. 그래도 JDBC 드라이버는 추상화 계층을 제공해 주니까 이걸 하이버네이트가 읽어 들여서 벤더에 적당한 타입으로 변환해준다.
  • 즉, 사용자는 타입 걱정하지 않아도 된다.
  • 문자열의 길이 제한 설정(length 속성 값)에 따라서 하이버가 적당한 타입으로 결정한다.
  • 이런 전략 자체도 사용자 맘대로 변경할 수 있다.
Java Type Hibernate Mapping Type SQL Type
int, Integer integer INTEGER
long, Long long BIGINT
short, Short short SMALLINT
float, FLOAT float FLOAT
double, Double double DOUBLE
BigDecimal big_decimal NUMERIC
String character CHAR(1)
String string VARCHAR
byte, Byte byte TINYINT
boolean, Boolean boolean BIT
boolean, Boolean yes_no CHAR(1) ('Y' or 'N')
boolean, Boolean true_false CHAR(1) ('T' or 'F')

날짜와 시간 맵핑

  • java.util.Date 타입을 timestamp로 맵핑할 경우 DB에서 나노초까지 값을 가져온다.
  • 책을 쓸 시점에서는 나노초를 짤라주지 않아서, equlas() 메소드로 비교할 경우 Date.getTime() 으로 비교하거나 Custom Mapping Type을 만들어서 사용해야 한다.
Java Type Hibernate Mapping Type SQL Type
java.util.Data, java.sql.Date date DATE
java.util.Date, Java.sql.Time time TIME
java.util.Date, java.sql.Timestamp timetamp TIMESTAMP
java.util.Calendar calendar TIMESTAMP
java.util.Calendar calendar_date DATE

바이너리와 큰 값 맵핑

  • binary 맵핑 타입만 식별자 속성으로 사용할 수 있다.
  • byte[]일 경우에는 VARBINARY로 맵핑하고, String일 경우에는 CLOB로 맵핑할 수 있다.
  • 이 두 경우에 Entity 객체가 속성 값을 읽어들일 때, 초기화를 하려고 할텐데, 너무 큰 값이라서 Lazy loading 하고 싶을 수 있다.
  • 특정 속성을 lazy loading 하는 두 가지 방법이 있다.
    • 13.1.6에서 다루는 Lazy loading with interception
    • java.sql.Clob 나 java.sql.Blob 타입을 사용한다.(로딩하는 시점에 Entity는 Persistent 상태여야 한다.)
  • JPA를 사용할 때 String 타입은 기본으로 VARCHAR로 맵핑 된다. Clob 타입으로 맵핑하려면 @Lob 애노테이션을 사용한다.
  • Serializable 타입은 변수의 값을 바이트 스트림으로 바꿔서 VARBINARY 타입 컬럼에 저장한다. 로딩할 때는 역직렬화한다.
Java Type Hibernate Mapping Type SQL Type
byte[] binary VARBINARY
String text CLOB
java.sql.Clob clob CLOB
java.sql.Blob blob BLOB
java.io.Serializable serializable VARBINARY

JDK 맵핑 타입

  • <property>만 type 속성을 가진 것은 아니다.
Java Type Hibernate Mapping Type SQL Type
java.lang.Class class VARCHAR
java.util.Locale locale VARCHAR
java.util.TimeZone timezone VARCHAR
java.util.Currency currency VARCHAR

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

Enum Type 만들기  (0) 2008.02.04
ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
top


Entity와 Value type 복습

Hibernate/Chapter 5 : 2008.02.04 00:16


특징

Entity Value Type
Coarse-grained Fine-grained
도메인 설명에 잘 나타난다. 잘 나타나지 않는다.
Persistent id를 가지고 있다. 그런거 없고, Entity 클래스에 종속된다.
상태(transient, detached, persistent)가 있다. 이런 상태를 신경쓰지 않는다.
자신만의 라이프 사이클을 가지고 있다. 라이프 사이클은 자신을 소유한 Entity를 따른다.
공유 된다. 공유되지 않는다. Entity는 고유한 Value Type을 가지고 있다.
@Entity로 맵핑한다. @Embeddable로 맵핑한다.

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

ParameterizedType 만들기  (0) 2008.02.04
CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
top


상속 맵핑 전략 선택하기

Hibernate/Chapter 5 : 2008.02.01 21:40


맵핑 전략

  • 다형적인 관계나 쿼리가 필요하지 ㅇ낳다면, Table Per Concrete Class를 사용하라. 즉, 상위 타입으로 객체들을 가져다 쓰지 않을거라면...
  • 다형적인 관계나 쿼리가 필요하고, 하위 클래스들의 속성이 몇 개 되지 않는 다면, Table Per Class Hierarchy를 사용하라.
  • 다형적인 관계나 쿼리가 필요하고, 하위 클래스들에 속성들이 많다면 Table Per SubClass를 사용하라.

특징

  • 간단한 경우에는 Table Per Class Hierarchy를 사용하라. 그 다음에 좀 더 복잡한 경우에 Table Per Subclass를 고려하라. 하지만 그렇게 하기 전에 상속구조를 Deligation으로 대체할 수 없을지 고민해 봐라.
  • 하이버네이트가 도메인과 관계형 모델의 버퍼 역할을 해주긴 하지만, 그렇다고 해서 Persistence Concern을 몰라도 된다는 뜻은 아니다.

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

CompositeUserType 만들기  (0) 2008.02.04
UserType만들기  (0) 2008.02.04
맵핑 타입 사용하기  (0) 2008.02.04
내장된 맵핑 타입  (0) 2008.02.04
Entity와 Value type 복습  (0) 2008.02.04
상속 맵핑 전략 선택하기  (0) 2008.02.01
상속 맵핑 전략 혼용하기  (0) 2008.02.01
Table per subclass  (0) 2008.02.01
Table per class hierarchy  (0) 2008.02.01
Table per concrete class  (0) 2008.02.01
Table per connrete class with implicit polymorphism  (0) 2008.02.01
top




: 1 : 2 : 3 : 4 :