Hibernate VS JPA

Hibernate JPA
One-To-One 연관 맵핑에서 공유하는 주키의 자동 생성을 지원한다. 표준화 된 One-To-One 맵핑을 지원한다. 하이버네이트 확장을 이용하면 공유하는 주키를자동 생성할 수 있다.
하이버네이트는 모든 엔티티 연관 맵핑을 Join Table로 할 수 있다. 표준화된 연관 맵핑은 2차 테이블을 사용해서 가능하다.
하이버네이트는 인덱스를 가진 엔티티 리스트를 맵핑할 수 있다. 하이버네이트 확장 애노테이션이 있어야 가능하다.
하이버네이트는 모든 경우에 다형적인 연관을 지원한다. Table Per Concrete Class With Implicit Polymorphism은 빼고는 전부 지원한다.

Table Per Concrete Class(with implicit polymorphism)에서 다형적인 연관

특징

  • Table Per Concrete Class With Implicite Polymorphism으로 맵핑했을 경우를 살펴본다.
  • 상위 타입과 OneToMany 콜렉션으로 맵핑할 수 없다.
  • 그래도 다형적인 쿼리를 사용하려면 핵을 사용해야 하는데, 이건 정말이지.. 최후의 선택이어야만 한다. 이러기 전에 union으로 설정을 바꾸는 것을 고려해보아라.
  • XML 설정에서는 핵을 사용해서 억지로 다형적인 연관을 맺게 할 수 있지만, SQL 테이블 조인식을 작성하기도 어려워지고, HQL도 그런 연관 맵핑을 지원하지 않는다. 그리고 JPA 애노테이션도 그런 설정을 지원하지 않는다.
  • 절대로 사용하지 말자. 따라서 패스.

Union을 사용한 다형적인 연관

특징

  • Table Per Subclass와 Table Per Class Hierarchy에서의 다형적인 연관을 확인했다.
  • Table Per Concrete Class With Union에서도 똑같이 적용된다.
콜렉션 테스트
@Test
public void oneTomanyConfirm() throws Exception {
Session session = sessionFactory.openSession();

User user = (User) session.get(User.class, 1l);
Set<BillingDetails> bds = user.getBilllingDetailses();
for(BillingDetails bd : bds){
System.out.println("..");
}

session.flush();
session.close();
}
단일 클래스 연관 테스트
@Test
public void oneToOneConfirm() throws Exception {
Session session = sessionFactory.openSession();

User user = (User) session.get(User.class, 1l);
BillingDetails bd = user.getDefaultBillingDetails();
bd.pay();

session.flush();
session.close();
}
  • @OneToOne 이나 @ManyToOne으로 상위 타입과 연관을 맺고 있을 때, 외례키 제약은
    하이버네이트가 만들어 줄 수 없다. 왜? 어떤 하위 클래스의 주키를 외례키로 가져야 하는지 판단하는게 쉽지 않다. 따라서
    사용자가 적당한 제약 사항을 만들어야 한다.

다형적인 콜렉션 연관

특징

  • Table Per Class Hierachy 또는 Table Per Subclass를 사용한 상속구조로 맵핑 했다면, 다음과 같이 해도 아무일 없다.
저장하기
CreditCard cc = new CreditCard();
cc.setNumber(ccNumber);
cc.setType(ccType);
cc.setExpMonth(...);
cc.setExpYear(...);
User user = (User) session.get(User.class, userId);
// Call convenience method that sets both sides of the association
user.addBillingDetails(cc);
// Complete unit of work
가져오기
User user = (User) session.get(User.class, userId);
for( BillingDetails bd : user.getBillingDetails() ) {
// Invoke CreditCard.pay() or BankAccount.pay()
bd.pay(paymentAmount);
}
  • @ManyToMany 와 @OneToMany의 기본 FetchMode는 LAZY다. 따라서, 위의 코드에서 이터레이터로 BillingDetails를 하나 가리키기 시작할 때 콜렉션을 가져온다.
  • 루프 돌지 않고, user.getBillingDetails() 호출하면, 쿼리는 날아가지 않는다.

다형적인 ManyToOne 연관

특징

  • 다형적인 연관 관계는 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다. 따라서 불필요한 쿼리가 생길 수도 있다.