본문 바로가기

백엔드 개발/JPA

[하이버네이트 유저 가이드 파헤치기] @Basic 어노테이션 & @Column 어노테이션 - 2.3.1 ~ 2.3.3

반응형

2.3 Basic types

Basic value type은 보통 단일 데이터베이스 칼럼과 집계되지 않는 단일 자바 타입(JAVA type)을 매핑한다. 

하이버네이트는 JDBC 명세서에서 추천하는 자연스러운 매핑을 따르는 여러가지 내장된 기본 타입(basic type)을 제공한다.

내부적으로 하이버네이트는 특정한 org.hibernate.type.Type을 자바 타입과 데이터베이스 칼럼로 매핑할 떄 BasicTypeRegistry를 사용한다.

원문
Basic value types usually map a single database column, to a single, non-aggregated Java type. Hibernate provides a number of built-in basic types, which follow the natural mappings recommended by the JDBC specifications.
Internally Hibernate uses a registry of basic types when it needs to resolve a specific org.hibernate.type.Type.

 

2.3.1 하이버네이트에서 제공되는 기본 타입(Basic Type)

 

Table 1. Standard BasicTypes

(표가 너무 길어서 몇가지 타입만 예시로 제시한다. 모든 종류의 타입 조회는 표 아래 링크를 참고)

표 전체 링크: https://docs.jboss.org/hibernate/orm/5.5/userguide/html_single/Hibernate_User_Guide.html#basic-provided

Hibernate type 
(org.hibernate.type package)
JDBC type Java type BasicTypeRegistry key(s)
StringType VARCHAR java.lang.String

string, java.lang.String

TextType LONGVARCHAR java.lang.String text
BooleanType BOOLEAN boolean, java.lang.Boolean boolean, java.lang.Boolean
IntegerType INTEGER int, java.lang.Integer integer, int, java.lang.Integer
DoubleType  DOUBLE double, java.lang.Double double, java.lang.Double

 

Table 2. Java 8 BasicTypes

Hibernate type 
(org.hibernate.type package)
JDBC type Java type BasicTypeRegistry key(s)
DurationType BIGINT java.time.Duration Duration, java.time.Duration
LocalDateTimeType TIMESTAMP java.time.LocalDateTime LocalDateTime, java.time.LocalDateTime
LocalDateType DATE java.time.LocalDate LocalDate, java.time.LocalDate
LocalTimeType TIME java.time.LocalTime LocalTime, java.time.LocalTime

Table 3. Hibernate Spatial BasicTypes

Hibernate type 
(org.hibernate.type package)
JDBC type Java type BasicTypeRegistry key(s)
JTSGeometryType depends on the dialect com.vividsolutions.jts.geom.Geometry jts_geometry, and the class names of Geometry and its subclasses
GeolatteGeometryType depends on the dialect org.geolatte.geom.Geometry geolatte_geometry, and the class names of Geometry and its subclasses
하이버네이트 Spatial 타입을 사용하기 위해, hibernate-spatial를 클래스 경로에 종속성으로 추가해야한다.
그리고 org.hibernate.spatial.SpatialDialect의 구현을 사용해야한다.

이 매핑들은  org.hibernate.type.BasicTypeRegistry라는 서비스에 의해 관리된다.

 org.hibernate.type.BasicTypeRegistry는 필수적으로 org.hibernate.type.BasicType instances의 이름이 키로 된 맵(Map)을 유지한다. 이 것이 이전의 표에서 "BasicTypeRegistry key(s)" 칼럼의 목적이다.

 

원문
These mappings are managed by a service inside Hibernate called the org.hibernate.type.BasicTypeRegistry, which essentially maintains a map of org.hibernate.type.BasicType (a org.hibernate.type.Type specialization) instances keyed by a name. That is the purpose of the "BasicTypeRegistry key(s)" column in the previous tables.

 

2.3.2 @Basic 어노테이션(The @Basic annotation)

엄격하게 말해서, 베이직 타입(Basic Type)은 javax.persistence.Basic 어노테이션으로 표시된다.

일반적으로, @Basic 어노테이션은 생략될 수 있다.

( 엔티티 속성에 @Basic 어노테이션을 표시 안해도 베이직 타입이라고 가정한다.)

아래 두가지 예시는 최종적으로 동일하다.

 

원문
Strictly speaking, a basic type is denoted by the javax.persistence.Basic
annotation. Generally speaking, the @Basic annotation can be ignored, as it is assumed by default. Both of the following examples are ultimately the same.

 

Example 3. @Basic declared explicitly

@Entity(name = "Product")
public class Product {

	@Id
	@Basic
	private Integer id;

	@Basic
	private String sku;

	@Basic
	private String name;

	@Basic
	private String description;
}

 

Example 4. @Basic being implicitly implied

@Entity(name = "Product")
public class Product {

	@Id
	private Integer id;

	private String sku;

	private String name;

	private String description;
}

Tip.

JPA 명세에서는 엄격하게 베이직 타입으로 표시될 수 있는 자바 타입을 제한한다.
아래는 베이직 타입으로 표시될 수 있는 자바 타입 목록이다.

  • 자바 기본 타입 (boolean, int, etc)
  • 자바 기본 타입을 감싸는 wrapper class (Java.lang.Boolean, java.lang.Integer, etc)
  • java.lang.String
  • java.math.BigInteger
  • java.math.BigDecimal
  • java.util.Date
  • java.util.Calendar
  • java.sql.Date
  • java.sql.Time
  • java.sql.Timestamp
  • byte[] or Byte[]
  • char[] or Character[]
  • enums
  • Serializable를 구현한 타입
    • JPA의 Serializable 타입을 위한 "지원"은 데이터베이스에 그들의 상태를 직접적으로 직렬화하는 것이다.

만일 공급자 이식성에 관심이 있다면, 단지 베이직 타입만을 사용해야한다.

JPA 2.1에서 도입된 javax.persistence.AttributeConverter는 이식성에 대한 문제 완화를 돕기위해 도입되었다.

이 주제의 더 많은 정보는 JPA 2.1 AttributeConverters를 봐라


@Basic 어노테이션은 2가지 속성을 정의한다.

 

optional - boolean (defaults to true)

  어노테이션을 표기한 속성에 null을 허용할지 말지를 정의한다.

  JPA는 이 속성을 "힌트"로 규정하며, 이것의 효과가 필수적으로 특별하게 필요하다는 것을 의미한다.

  하이버네이트는 타입이 기본 타입이 아닌 동안, optional 속성을 기본 칼럼이 NULLABLE이 되어야함을 의미하기 위해 취한다.

 

fetch - FetchType (defaults to EAGER)

  어노테이션을 표기한 속성이 데이터를 즉시 가져올지 지연시켜서 속성값을 조회할 떄 가지고 올지 정의한다.

  JPA는 EAGER 설정이 엔티티가 값을 가져올때 값도 같이 가져와져야한다는 구현체(하이버네이트)에 대한 요구사항이라고 말한다.

  LAZY는 단지 속성에 접근할 떄 값을 가져오도록 하는 힌트이다.

  하이버네이트는 기본 타입에 대한 LAZY 설정을 무시한다. 

  단 bytecode enhancement를 사용하는 경우는 LAZY 설정을 사용할 수 있다.

  추가적인 bytecode enhancement과 fetch의 정보는  Bytecode Enhancement를 참고해라.

 

원문
The @Basic annotation defines 2 attributes.

optional - boolean (defaults to true)
 Defines whether this attribute allows nulls. JPA defines this as "a hint", which essentially means that its effect is     specifically required. As long as the type is not primitive, Hibernate takes this to mean that the underlying column     should be NULLABLE.

fetch - FetchType (defaults to EAGER)
Defines whether this attribute should be fetched eagerly or lazily.
JPA says that EAGER is a requirement to the provider (Hibernate) that the value should be fetched when the owner is fetched, while LAZY is merely a hint that the value is fetched when the attribute is accessed. Hibernate ignores this setting for basic types unless you are using bytecode enhancement. See the Bytecode Enhancement for additional information on fetching and on bytecode enhancement.

 

2.3.3 @Column 어노테이션 (The @Column annotation)

JPA는 암묵적으로 테이블과 칼럼 명명 규칙을 정의한다.

암시적인 명명에 대한 자세한 논의는  Naming strategies를 참고해라.

 

기본 타입 속성의 경우 암시적인 명명 규칙은 칼럼 이름이 속성 이름과 동일하다.

만일 암시적인 명명 규칙이 당신의 요구사항을 충족하지 못 한다면,

당신은 명시적으로 사용할 하이버네이트 칼럼 이름을 하이버네이트에게 알려줄 수 있다.

 

원문
JPA defines rules for implicitly determining the name of tables and columns. For a detailed discussion of implicit naming see Naming strategies.

For basic type attributes, the implicit naming rule is that the column name is the same as the attribute name. If that implicit naming rule does not meet your requirements, you can explicitly tell Hibernate (and other providers) the column name to use.

 

Example 5. Explicit column naming

@Entity(name = "Product")
public class Product {

	@Id
	private Integer id;

	private String sku;

	private String name;

	@Column( name = "NOTES" )
	private String description;
}

 

여기서 우리는 명시적으로 엔티티의 description 속성을 NOTES 칼럼과 매핑하기 위해 @Column 어노테이션을 사용했다.

@Column 어노테이션은 다른 매핑 정보 역시 규정한다. 상세한 것들을 알고 싶으면 @Column 어노테이션의 문서를 읽어라.

 

원문
Here we use @Column to explicitly map the description attribute to the NOTES column, as opposed to the implicit column name description.
The @Column annotation defines other mapping information as well. See its Javadocs for details.

 

하이버네이트 유저 가이드 원문


하이버네이트 유저 가이드에서는 @Column 어노테이션은 간단하게 언급했다.
하지만 실무에서 자주 사용하는 어노테이션이므로 필자가 추가 설명을 하겠다.

@Column 어노테이션 속성

참고로 아래 속성들은 모두 필수값이 아니다. optional이다.

자주 사용되는 속성에는 굵은 표시를 하였다.

속성명 설명 기본값
name 칼럼의 이름
기본적으로는 속성명이나 필드명이 칼럼명으로 설정된다.
""
unique 칼럼이 unique key인지 아닌지 설정한다. false
nullable 칼럼이 nullable인지 아닌지 설정한다. true
insertable 하이버네이트가 생성한 SQL INSERT 문에 해당 칼럼을 포함하는지 아닌지 설정한다. true
updatable 하이버네이트가 생성한 SQL UPDATE 문에 해당 칼럼을 포함하는지 아닌지 설정한다. true
columnDefinition 해당 칼럼의 DDL이 생성될 때 사용되는 SQL의 일부이다. ""
table 칼럼을 포함하는 테이블의 이름.
만일 칼럼을 빠트린다면 기본 테이블에 있는 것으로 간주됩니다.
""
length 칼럼의 데이터 길이
(string 값인 칼럼에만 적용된다.)
255
precision 소수 (정확히는 numeric) 칼럼을 위한 정확도
(decimal 칼럼에서 사용될 때 적용된다.)
총 유효 자리수 (소수점 자리수 포함)
0
scale 소수 (정확히는 numeric) 칼럼을 위한 스케일
(decimal 칼럼에서 사용될 때 적용된다.)
소수점 오른쪽 자리수
0

 

예시

columnDefinition 사용

@Entity
public class User {
    @Id
    Long id;

    @Column(columnDefinition = "varchar(255) default 'John Snow'")
    private String name;

    @Column(columnDefinition = "integer default 25")
    private Integer age;

    @Column(columnDefinition = "boolean default false")
    private Boolean locked;
}

위 엔티티가 테이블로 생성될 때의 DDL

create table user
(
    id     bigint not null constraint user_pkey primary key,
    name   varchar(255) default 'John Snow',
    age    integer      default 35,
    locked boolean      default false
);

 

insertable, updatable 사용 예시

 

1. view와 엔티티를 매핑하는 경우

아래 예시는 가상 테이블 (view)과 @SecondaryTable을 이용해 테이블을  하나의 엔티티에 매핑한 예시이다.

view에서 가져온 칼럼인 viewColumn에는 insert와 update를 해서는 안되므로 insertable, updatable를 false로 설정했다.

그리고 테이블(THE_TABLE)에서 가져온 칼럼인 tableColumn은 table 속성을 설정해서 매핑하고 있는 것을 알 수 있다.

@Entity
@Table(name = "THE_VIEW")
@SecondaryTable(name = "THE_TABLE", pkJoinColumns = @PrimaryKeyJoinColumn(name = "THE_ID"))
public class MyEntity {

    @Id
    @Column(name = "THE_ID")
    private Integer id;

    @Column(name = "VIEW_COLUMN", updatable = false, insertable = false)
    private String viewColumn

    @Column(name = "TABLE_COLUMN", table = "THE_TABLE")
    private String tableColumn;
 }

 

2. 데이터가 입력될 때의 일시를 기록하는 timestamp 칼럼에 적용

아래 예시에서 created_at 속성은 테이블에 Insert 문으로 개별 row가 입력될 때의 일시를 기록하는 칼럼이다.

그러므로 코드를 통해서 일시가 바뀌는 것을 방지해야해서 updatable를 false로 설정했다. 

@MappedSuperclass
@EntityListeners({AuditingEntityListener.class})
@Getter
public class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime created_at;

    @LastModifiedDate
    private LocalDateTime updated_at;
}

 

그 밖에 name이나 length 속성도 자주 사용하지만 사용이 직관적이므로 설명은 생략한다.

각자의 상황에 맞게 칼럼명과 문자열의 크기를 제한하는데 사용하길 바란다.


참고 자료

https://www.baeldung.com/jpa-default-column-values

https://stackoverflow.com/questions/3805584/please-explain-about-insertable-false-and-updatable-false-in-reference-to-the-jp

https://velog.io/@aidenshin/%EC%97%94%ED%8B%B0%ED%8B%B0-%EC%9E%91%EC%84%B1-JPA-Auddit-%EC%A0%81%EC%9A%A9

반응형