기본 속성 맵핑하기- 실습

기본값 설정하는 방법을 테스트합니다.
– @Column의 columnDefinition 속성을 사용해서 column을 만들 때 사용할 공식을 사용할 수 있습니다.
– 하이버의 @GenerationTime 애노테이션을 사용해서 기본값이나 생성되는 값을 언제 생성할 지 설정할 수 있습니다.

1. 테스트 코드

    @Test
    public void add() throws Exception {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        Member member = new Member();
        member.setName(“썬”);

        session.save(member);
        session.flush();

        transaction.rollback();
        session.close();
        assertNotNull(member.getId());
        assertEquals(new Integer(1), member.getDefaultNum());
    }

2. 결과 쿼리

Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into Member (name, id) values (?, ?)
Hibernate: select member_.defaultNum as defaultNum0_ from Member member_ where member_.id=?

3. Persistent Class

@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @Column(columnDefinition = “number(10,2) default ‘1’”)
    @org.hibernate.annotations.Generated(org.hibernate.annotations.GenerationTime.INSERT)
    private Integer defaultNum;

    …

}

위 테스트 코드에서 flush를 하지 않으면 시퀀스만 가져오고 insertion은 이뤄지지 않습니다. insertion을 안하기 때문에 db가 생성하는 기본값을 가져오기 위해 발생하는 세번째 쿼리도 실행하지 않습니다. 따라서 다음의 SQL 한 문장만 실행합니다.

Hibernate: select hibernate_sequence.nextval from dual

참조 : 4.4.1 Mapping basic properties/Generated and default property values

Hibernate VS JPA

Hibetnate JPA
Entity와 Value-type 개념은 rich and fine-grained domain model을 지원하기 위한 필수 요소다. Value-type을 embeddable classes 라고 부른다. nonportable.
10가지나 되는 식별자 생성기 제공한다. 4가지만 지원한다. 벤더가 확장할 수 있도록 지원한다.
필드, Accessor 메소드, PropertyAccessor 인터페이스 구현체를 통해서 접근 가능하다. 혼용할 수 있다. 속성이나, Accessor 메소드로 접근할 수 있는데, 하이버네이트 애노테이션 없이 혼용할 수는 없다.
formula와 DB가 생성한 값을 사용할 수 있다. JPA에는 이런 거 없다. 하이버가 필요하다.

컴포넌트 맵핑하기

특징

  • 자바 객체에서 Value-type과 Entity를 구분지을 수 있는것은 식별자 속성 뿐이다.
  • 컴포넌트와 Entity 간의 양방향 관계를 설정할 수 있다.
  • 컴포넌트가 다른 컴포넌트나 Entity를 가지고 있을 수 있다.
    • This flexibility is the foundation of Hibernate’s support for finegrained object models.

설정하기

  • 컴포넌트에는 @Embeddable Entity의 속성에는 @Embedded 사용하기
  • @org.hibernate.annotations.Parent 애노테이션으로 컴포넌트에서 back-pointer 프로퍼티 사용 가능.
  • if you store a component object with all null property
    values, Hibernate returns a null component when the owning entity
    object is retrieved from the database.
컴포넌트 사용하기
@Entity
@Table(name = "USERS")
public class User {
@Embedded
private Address homeAddress;


}
컴포넌트 클래스 정의하기
@Embeddable
public class Address {

@Column(name="ADDRESS_STREET", nullable=false)
private String street;

@Column(name="ADDRESS_ZIPCODE", nullable=false)
private String zipcode;

@Column(name="ADDRESS_CITY", nullable=false)
private String city;

}
  • 컴포넌트를 사용하는 쪽에서 컴포넌트의 컬럼 정의를 재정의 할 수도 있다.
  • 단점
    • First,shared references, as for all value types, aren’t possible. -> 이건 value-type 이니까 당연히 그래야 하는거 아닌가.
    • Second, there is no elegant way to represent a null reference to an Address. -> 흠.. new Address()를 항상 가지고 있어야겠군.

기본 속성 맵핑하기

특징

  • @Transient 애노테이션을 붙이거나 자바의 transient 키워드를 사용하지 않으면 기본으로 전부 Persistent 대상으로 인식한다.
  • 애노테이션을 붙이지 않으면 다음과 같은 규칙이 적용된다.
    • JDK가 제공하는 타입일 경우에 자동으로 Persistent 대상이 된다.
    • 그렇지 않고, 속성의 타입에 해당하는 클래스에 @Embeddable 애노테이션이 붙어있는 경우, 컴포넌트를 가지고 있는 형태로 맵핑된다.
    • 그렇지도 않고, 속성의 타입이 Serializable 인터페이스를 구현한 경우, 직렬화된 형태로 DB에 저장된다. (이러길 원치 않을 것이다.)

설정하기

  • Chapter 17에서 하이버네이트 애노테이션으로 DDL을 작성하거나 동적으로 검증하는 기능에 대해 다룬다.
  • 클래스 속성에 접근하는 방법은 @Id 애노테이션이 붙어있는 위치가 기본 방법으로 설정 된다.(JPA)
  • 하이버네이트는 이 기본 방법을 변경할 수 있는 기능을 제공한다. @org.hibernate.annotations.AccessType(<strategy>)
    • 클래스에 AccessType 애노테이션을 사용하면, @Id로 인해 설정되는 접근 전략을 무시하며, 해당 클래스의 필드나 메서드에 붙어있는 모든 애노테이션들은 AccessType에서 정의한 접근 전략을 따른다.
    • 기본 상태이거나 필드 접근인 상태에서 @AccessType(“property”)를 필드 위에 붙여주면, 해당 속성을 accessor 메소드들을 이용해서 접근한다.
    • 기본 상태이거나 Accessor 접근이 상태에서 @AccessType(“field”)를 getter 위에 붙여주면, 해당 속성은 필드에 바로 접근한다.
    • @Embedded 클래스는 자신을 가지고 있는 쪽 클래스의 접근 방법을 따른다.
    • @MappedSuperclass 클래스는 맵핑 된 클래스의 접근 방법을 따른다.
  • noop 방식: “virtual” property in HQL queries. to use the database column in HQL queries only.
  • 마음에 드는게 없으면 org.hibernate.property.PropertyAccessor 인터페이스 구현해서 직접 만들어 됨.
    • 사용할 때는 @AccessType 애노테이션의 access 속성에 패키지 이름을 붙인 클래스 이름을 써주면 돼.

계산이 필요한 속성

  • 맵핑되는 컬럼은 없다.
  • @Fomula 애노테이션 사용하기
필드에서 계산된 속성 사용하기
@org.hibernate.annotations.Formula("TOTAL + TAX_RATE * TOTAL")
public BigDecimal getTotalIncludingTax() {
return totalIncludingTax;
}
  • SQL 함수도 사용할 수 있다.

h1, DB에서 생성된 값과 기본 값

  • public 세터를 만들지 말아라.
  • @Generated 애노테이션 사용하기
DB에서 생성된 값 사용하기
@Column(updatable = false, insertable = false)
@org.hibernate.annotations.Generated(
org.hibernate.annotations.GenerationTime.ALWAYS
)
private Date lastModified;
  • 기본값이 설정되도록 하려면 flush 해야한다.
  • columnDefinition 속성으로 기본값 설정하기
기본값 설정하기
@Column(name = "INITIAL_PRICE",
columnDefinition = "number(10,2) default '1'")
@org.hibernate.annotations.Generated(
org.hibernate.annotations.GenerationTime.INSERT
)
private BigDecimal initalPrice;
  • columnDefinition: complete properties for the column DDL, with datatype and all constraints.
    • DDL customization은 8장에서 다룬다.

모르는 것

  • @MappedSuperclass는 뭔가?

작명 지침 사용하기

특징

  • Hibernate provides a feature that allows you to enforce naming standards automatically.

설정하기

  • NamingStrategy 인터페이스 구현하기
  • ImprovedNamingStrategy 클래스 상속받아서 구현하기
네임스페이스 등록하기
import org.hibernate.cfg.ImprovedNamingStrategy;
import org.hibernate.util.StringHelper;

public class CENamingStrategy extends ImprovedNamingStrategy {
public String classToTableName(String className) {
return StringHelper.unqualify(className);
}

public String propertyToColumnName(String propertyName) {
return propertyName;
}

public String tableName(String tableName) {
return "CE_" + tableName;
}

public String columnName(String columnName) {
return columnName;
}

public String propertyToTableName(String className, String propertyName) {
return "CE_" + classToTableName(className) + '_'
+ propertyToColumnName(propertyName);
}
}
  • classToTableName(): <class> 맵핑에서 table 속성에 값을 입력하지 않았을 때 호출.
  • propertyToColumnName(): 명시적인 column 이름을 설정하지 않았을 때 호출.
  • tableName(), columnName(): 명시적으로 이름을 설정했을 때 호출.
  • 동적으로 설정을 바꿀 수 있음.
동적으로 네임스페이스 바꾸기
Configuration cfg = new Configuration();
cfg.setNamingStrategy( new CENamingStrategy() );
SessionFactory sessionFactory sf =
cfg.configure().buildSessionFactory();

모르는 것

  • SessionFactory 설정할 때 구현한 클래스를 등록해 주면 되는건가?