Entity Mapping

엔티티 정보와 이에 대응되는 클래스를 매핑 annotation을 이용해서 매핑 시켜줘야한다.

@Entity

JPA에서 사용하는 테이블과 매핑되는 클래스를 맵핑한다.

@Entity
public class Member{
  public Member(){
    ...
  }
}
  • 기본 생성자는 필수. (파라미터가 없는 public 또는 protected 생성자)
  • final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
  • 저장할 필드에 final을 사용하면 안된다.

왜 기본생성자가 필수 일까?

JPA에서는 Reflection API를 통해 빈 객체를 생성하게 되는데, 이때 인자 정보에 대해서는 접근할 수 없다. 따라서 인자를 필요로 하지 않은 기본 생성자가 필요한 것이다.

//Music Class
public class Music {
    private String singer;
    private String title;

    public Music(String singer, String title) {
        this.singer = singer;
        this.title = title;
    }
    public Music(){

    }

    public String getTitle() {
        return title;
    }

    public String getSinger() {
        return singer;
    }
}

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Object music = new Music("IU", "YOU AND ME");

    Class clazz = Music.class;
    Method getTitle = clazz.getMethod("getTitle");
    String title = (String)getTitle.invoke(music,null);
    System.out.println(title);
}

@Table

엔티티와 매핑되는 테이블을 지정한다.

@Entity
@Table(name="MEMBER")
public class Member{
  public Member(){
    ...
  }
}

DB Schema Auto Creation

JPA에서는 데이터베이스의 스키마를 자동 생성할 수 있는 기능을 제공한다. 클래스에 설정된 annotation 매핑 정보를 토대로, Table, Field 값을 분석해서 이에 대한 ddl을 자동 생성해서 DB에 적용한다.

persistence.xml

persistence.xml 에 아래의 2가지 설정을 추가한다.

<!-->DDL을 자동 생산<!-->
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-->콘솔에 생선된 SQL문 출력<!-->
<property name="hibernate.show_sql" value="true" />

위와 같이 설정되며 Table 정보를 참고해서 DDL문을 자동으로 생성해주게 된다. 하지만, 운용환경에서 사용할 만큼 완벽한 DDL은 아니어서 개발환경에서만 사용하거나 참고용으로만 활용한다.

hibernate.hbm2ddl.auto 관련 설정 정보

option description
create 기존 테이블을 삭제하고 새로 생성, DROP+CREATE
create-drop 애플리케이션 종료시 생성한 DDL 제거,DROP + CREATE + DROP
update 데이터베이스의 테이블과 엔티티 매핑벙보를 비교해서 변경사항만 적용
validate update와 유사하게 작동하지만, 차이점에 대해 경고만 하고 실제 적용 안함

create,create-drop,update 같이 schema를 변경시키는 sql문들은 개발환경에서만 사용될 수 있도록 한다.

이름 매핑 전략

<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />

위와 같이 설정하게 되면 DB에서 SQL문 생성시 컬럼명을 자동적으로 언더바 형식으로 생성한다

보통, JAVA에서는 CamelCase를 활용하고, DB에서는 언더바 방식을 활용한다.

DDL 생성 기능

@Entity
@Table(name="MEMBER")
public class Member {

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "NAME", nullable = false, length = 10) //추가
    private String username;
    ...
}

위의 매핑정보를 토대로 아래의 sql문이 생성된다.

create table MEMBER (
    ID VARCHAR() PRIMARY KEY,
    NAME varchar(10) not null
)

column annotation을 이용해서 제약조건을 추가할 수 있다.

nullable=false를 설정하면 컬럼에 not null 속성 추가

length를 이용해서 최대 길이 지정

다음과 같이 유니크 제약조건을 만들어주는 것도 가능하다.

@Table(name="MEMBER", uniqueConstraints = {@UniqueConstraint( //추가 //**
        name = "NAME_AGE_UNIQUE",
        columnNames = {"NAME", "AGE"} )})

위와 같이 설정하면 아래의 sql문이 생성되고

ALTER TABLE MEMBER
ADD CONSTRAINTS NAME_AGE_UNIQUE UNIQUE(NAME,AGE);

와 같이 유니크 제약조건이 추가된다. 제약조건은 테이블 단위로 적용되므로 @Table annotation에 포함되게 된다.

Primary Key Mapping

기본키에 대해서 사용자가 직접 기본키값을 입력하는 방식과, DB에서 자동으로 생성한 값을 부여하는 방식이 존재한다.

직접 할당 방식

아래와 같이, @Id annotation만 추가하면 이를 기본키로 매핑하게 된다.

@Id
@Column(name = "ID")
private String id;

기본키를 적용할 수 있는 자료형 종류

  • 자바 기본형
  • 자바 래퍼형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

반드시, DB에 저장되기 전에 식별자(기본키)에 값을 할당해야된다. 그렇지 않으면 에러가 발생하게 된다.

자동 할당 방식

DB마다 자동 할당을 지원하는 방식이 다르므로 사용하는 DB에 맞게 방식 설정을 달리 해야한다.

우선 자동으로 기본키 값을 할당하는 방식을 사용하려면 아래의 property를 추가해줘야한다.

persistece.xml

<property name="hibernate.id.new_generator_mappings" value="true" />

추가로, 자동 할당 방식을 사용하기 위해서는 @GeneratedValue annotation을 추가하고, Generation Type를 각각의 방식에 맞게 설정해줘한다.

@GeneratedValue(strategy=GenerationType.IDENTITY)

IDENTITY 방식

IDENTITY 방식은 기본 키 생산을 DB에 위임하는 방식으로, MySQL,PostgreSQL, SQL Server, DB2에서 사용되는 방식이다.

MySQL의 auto_increment을 사용하는도 이를 활용하는 방식 중에 하나이다.

IDENTITY 방식의 자동할당을 활용하려면 아래와 같이 설정한다.

@Entity
public class Board {
  @Id 
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  private int id;
}
Board board=new Board();
em.persist(board);
System.out.println("board.id=" + board.getId());

기본키를 DB에서 자동으로 할당 해주기 때문에, JPA는 ID값을 알아내기 위해 DB에 쿼리를 추가로 날리게 된다. 또한, em.persist() 호출 과정에서 식별자값이 할당되지 않은 상태이므로 이 INSERT 쿼리는 영속성 컨텍스트에 저장되기 전에 바로 DB에 쿼리가 날려진다. –> 지연 쓰기가 적용되지 않는다.

SEQUENCE 방식

유일한 값을 순서대로 생성하는 DB 오브젝트를 이용하는 방식으로 Oracle ,PostgreSQL, SQL Server, DB2, H2 에서 사용할 수 있다.

아래와 같은 SEQUENCE가 있다고 하자

CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;

SEQUENCE 매핑은 아래와 같이 수행한다.

@Entity
@SequenceGenerator(
    //SequenceGenerator의 이름 설정
    name = "BOARD_SEQ_GENERATOR",
    //DB Sequence의 이름
    sequenceName = "BOARD_SEQ",
    //초기값
    initialValue = 1,
    //증가값
    allocationSize = 1)
public class Board {

    @Id
    //SEQUENCE 방식으로 설정하고, 위에서 생성한 generator 값을 부여
    @GeneraedValue(strategy = GenerationType.SEQUENCE,
                    generator = "BOARD_SEQ_GENERATOR")
    private Long id;
}

DB 시퀀스를 이용해서 기본키 값을 받아와서 entity 식별자에 값을 할당하고, 이를 영속성 컨텍스트에 저장하게 된다.

IDENTITY 방식과는 달리, INSERT를 바로 실행하는 것이 아니라, 기본키만 먼저 조회 한다음, 이를 엔티티에 부여해서 영속성 컨텍스트에 저장하게 된다. 이후에 플러시 될때 DB에 적용된다. –> 지연 쓰기 동작

TABLE 방식

키 전용 테이블을 만들어, 데이터 베이스 시퀀스 인것 처럼 작동하게 하는 것이다. 일반 테이블을 만들어서 사용하는 것이므로 모든 DB에서 활용가능하다.

키 자동 생성용 테이블

create table MY_SEQUENCES (
    -- sequence의 이름을 저장하는 필드
    sequence_name varchar(255) not null,
    -- 해당 sequence의 다음 값을 저장하는 필드
    next_val bigint,
    primary key (sequence_name)
)

매핑 수행

@Entity
@TableGenerator(
    name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,
                generator = "BOARD_SEQ_GENERATOR")
    private Long id;
}

다음과 같이 설정하게 되면

MY_SEQUENCES 테이블에 아래와 같이 값이 생성되게 된다.

sequence_name next_val
BOARD_SEQ 1

여러개의 sequence를 관리하는 테이블 처럼 작동하게 된다.

SEQUENCE와 비슷하게, 영속성 컨텍스트에 저장하기 전에 MY_SEQUENCES 테이블로의 SELECT, UPDATE 문을 수행하기 위해 DB로의 접근이 필요하다

AUTO 방식

AUTO는 DB에 맞춰서 자동으로 방식을 선택해서 적용하는 것이다.

Oracle –> SEQUENCE MySQL –> IDENTITY

@Entity
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
}

DDL 자동 생성을 활용하면 DB가 알아서 Sequence나 키 자동생성용 테이블을 만들게 된다.

Field Column Mapping

@Column

컬럼과 관련된 설정들을 할 수 있다.

Options Description
name 컬럼의 이름을 설정
nullable null 허용가능 여부 설정
unique 컬럼에 유일값 제약조건 적용 여부
length 길이 제한

요구조건에 맞춰서 옵션을 설정해주면된다.

Example

not null 이면서 unique 한 컬럼 설정

@Column(nullable=false, unique=true)
private String username;

위의 매핑 정보가 아래의 SQL문을 생성하게 된다.

USERNAME VARCHAR() NOT NULL, UNIQUE

@Enumerated

java의 enum 타입을 맵핑할 수 있다.

EnumType 값 종류

  • EnumType.ORDINAL을 이용하게 되면 enum 순서대로 값을 저장
    • ADMIN은 1, USER는 2
    • enum의 순서를 바꾸게 되면 기존의 DB에 저장되어 있는 정보에 의미가 바뀌게 된다.
  • EnumType.String 이용하게 되면 enum 이름을 저장
    • ADMIN은 “ADMIN”, USER는 “USER”
    • DB에 저장되는 값의 크기가 ORDINAL 방식보다는 크다.

목적에 맞춰서 사용하면된다. 일반적으로 EnumType.String의 사용을 권한다.

Example

enum RoleType{
  ADMIN,USER
}

@Enumerated(EnumType.String)
private RoleType roleType;

@Temporal

날짜 타입을 매핑할 때 사용한다.

Options Description
TemporalType.DATE 날짜 타입, DB의 Date 타입과 매핑
TemporalType.TIME 시간 타입, DB의 Time 타입과 매핑
TemporalType.TIMESTAMP 날짜_시간 타입, DB의 TIMESTAMP 타입과 매핑

Example

@Temporal(TemporalType.DATE)
private Date date;

@Temporal(TemporalType.TIME)
private Date time;

@Temporal(TemporalType.TIMESTAMP)
private Date timestamp;

위와 같은 매핑 정보는 아래의 SQL로 변환된다.

date DATE,
time TIME,
timestamp TIMESTAMP 

@Lob

DB의 BLOB, CLOB와 매핑된다.

해당 annotation은 따로 설정할 수 있는 옵션은 없는데, java의 자료형을 보고 CLOB 또는 BLOB으로 할당한다.

String,char[] 와 같은 타입은 CLOB으로

byte[] 형태는 BLOB으로 변환한다.

Example

@Lob
private String lobString

@Lob
private byte[] lobByte;

이를 sql로 표현하게 되면 아래와 같다.

lobString CLOB,
lobByte BLOB

@Transient

DB에 저장하지 않는 컬럼을 지정하기 위한 필드, 해당 annotation 설정 시 해당 변수는 컬럼으로 생성되지 않는다.

References

book: 자바 ORM 표준 JPA 프로그래밍 -김영한 저

book_link

태그: ,

카테고리:

업데이트:

댓글남기기