본문 바로가기

JPA

[JPA] 엔티티 매핑

      JPA가 하는일 중 가장 중요한 것은 엔티티를 테이블에 매핑하는 것이다. JPA는 다양한 어노테이션을 통해 엔티티-테이블 매핑을 지원한다. 이번 포스팅에서는 엔티티 매핑을 위한 어노테이션과 테이블 매핑 방법에 대해 알아보자.

     


    엔티티 매핑

    엔티티-테이블 매핑을 지원하는 어노테이션은 크게 4가지로 나뉜다.

     

    • 객체-테이블 매핑 : @Entity, @Table
    • 기본 키 매핑 : @Id
    • 필드와 칼럼 패밍 : @Column
    • 연관관계 매핑 : @ManyToOne, @JoinColumn ...

     

    연관관계 매핑의 경우 단순한 어노테이션을 넘어 데이터베이스에서 굉장히 중요한 개념으로 따로 다루기로 하고, 이번 포스팅에서는 그외 3가지를 차례대로 알아보도록 하자.


    객체-엔티티 매핑 : @Entity

    JPA의 세계에서 보통 하나의 객체는 하나의 엔티티가 된다. 엔티티란 JPA가 관리하는 객체라고 생각하면 쉽다. 이때 객체를 엔티티로 매핑해주는 것이 @Entity 어노테이션이다. 객체(클래스)에 @Entity 어노테이션을 지정해주면 해당 객체는 엔티티가 되어 JPA가 관리하게 된다.

     

    @Entity의 자주 쓰는 속성은 'name' 으로 JPA에서 사용할 엔티티의 이름을 지정한다.

     

    @Entity의 속성

    속성 기능 default
    name JPA에서 사용할 엔티티의 이름을 지정한다. 보통 기본값인 클래스의 이름을 사용한다. 만약 같은 이름의 엔티티가 다른 패키지에 있을 경우, 충돌하지 않도록 이름을 직접 지정해주어야 한다. 클래스의 이름 (e.g. Member)

     

     

    c.f.) 엔티티(entity)와 테이블(table)의 차이

    JPA를 다루다보면 엔티티와 테이블이 혼용되어 사용되는 경우가 많다. 둘의 차이는 개념 계층의 차이이다. 엔티티는 논리적인 개념(Logical Concept)으로 여러 속성의 집합 정도로 생각하면 쉽다(실제 물리적으로 존재하는 것이 아님) 이것이 DB에 저장되면 비로소 물리적 개념(Physical Concept)인 테이블이 되는 것이다(실제 존재함)

    모든 엔티티가 테이블이 되지 않지만 대부분의 엔티티는 DB에 생성되면 테이블이 된다. 

     


    엔티티-테이블 매핑 : @Table

    엔티티와 매핑할 테이블을 지정할때는 @Table 어노테이션을 사용한다. 생략할 경우 엔티티의 이름을 테이블의 이름으로 사용한다.

     

    다음은 자주쓰는 @Table의 속성들이다

     

    @Table의 속성

    속성 기능 default
    name 매핑할 테이블의 이름 클래스의 이름
    catalog catolog 기능이 있는 DB에 catalog를 매핑  
    scheme scheme 기능이 있는 DB에서 scheme을 매핑  
    uniqueConstraints DDL 생성시 유니크 제약조건을 만든다. 스키마 자동 생성 기능을 통해 DDL을 만들때만 성립  

     

    사실 테이블 이름과 엔티티 이름이 다른 경우는 많지 않다. 그러므로 @Table 어노테이션은 생략하는 경우가 많다. 하지만 만약 테이블 이름을 다르게 지정하고 싶다거나 같은 이름의 테이블이 존재해 충돌할 위험이 있을 경우 직접 @Table 어노테이션을 지정해주어야 한다.


     

    기타 : @Id / @Column

    테이블을 매핑했으면 테이블의 기본 키나 칼럼들을 엔티티의 필드와 매핑해야 한다. 이때 @Id / @Column을 사용하면 된다.

     

    @Entity
    @Table(name = "MEMBER")
    public class Member {
    	
        @Id
        @Column(name = "ID")
        public Long id;
        
        @Column(name = "NAME")
        public String name;
        
        public int age
        
        @Temporal(TemporalType.TIMESTAMP)
        public Date birth;
        
        @Enumerated(EnumType.STRING)
        public Status status;
    }

     

    @Id를 지정하여 id 필드를 기본 키(pk)로 지정하였다.

     

    @Column의 경우 필드의 이름을 기본값으로 사용하므로 만약 필드의 이름과 칼럼명이 같다면 생략해도 된다.

     

    @Temporal 어노테이션의 경우 날짜 타입을 매핑할때 사용한다.

    @Enumerated 어노테이션의 경우 enum 타입을 매핑할때 사용한다. 자세한 건 레퍼런스 참고.

     

    이외에도 다양한 기능을 지원하는 어노테이션이 많다. 하지만 그것들을 다 외울수는 없으므로 사용할때마다 레퍼런스를 참고하면 된다.


    데이터베이스 스키마 자동생성

    JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다. 위에서 설명한 어노테이션을 통해 클래스의 매핑 정보를 확인하고, JPA는 이 클래스의 매핑정보와 데이터베이스 방언(dialect)를 통해 데이터베이스 스키마를 생성한다.


    테이블 자동 생성

     

    스키마 자동생성 기능을 사용하려면 일단 persistence.xml(maven 프로젝트 기준) 파일에 아래 속성을 추가해주어야 한다.

    <property name="hibernate.hbm2ddl.auto" value="create" />

    다음 속성을 통해 프로젝트의 실행 시점에서 자동으로 테이블을 생성해준다.

     

    만약, 테이블을 생성하는 DDL(Date Definition Laugauge)을 보고 싶다면 hibernate.show_sql 속성을 true로 설정해주면 된다.

    <property name="hibernate.show_sql" value="true" />

     

    아래는 애플리케이션을 실행시 Member 엔티티를 테이블로 생성하는 DLL을 출력한 모습이다.

    Hibernate: 
        create table member (
            id bigint not null,
            age integer not null,
            birth timestamp,
            name varchar(255),
            primary key (id)
        )

     


    hibernate.hbm2ddl.auto 속성 주의 사항

     

    hibernate.hbm2ddl.auto 속성은 다양한 옵션을 제공하는데 옵션별 기능은 다음과 같다.

    옵션 기능
    create 기존 테이블을 삭제하고 새로 생성한다.
    create-drop create 속성 + 애플리케이션 종료시 생성한 DDL을 제거한다.
    update 데이터베이스 테이블과 엔티티를 비교하여 변경 사항만 수정한다.
    validate 데이터베이스 테이블과 엔티티를 비교하여 변경 사항이 있으면 경고를 남기고 실행하지 않는다.
    none 유효하지 않은 옵션값. 자동 생성기능을 사용하지 않는다.

     

    운영서버에서는 DDL을 수정하여 값을 바꾸는 create, create-drop, update 속성을 사용하면 안된다. 이러한 옵션들을 오직 개발단계에서만 사용해야 한다.

     

    개발 환경에 따른 추천 전략

    • 개발 초기 단계에는 create 또는 update
    • 초기화 상태로 자동화된 테스트를 진행하는 개발자 환경과 CI 서버는 create 또는 create-drop
    • 테스트 서버는 update 또는 validate
    • 운영 서버는 validate 또는 none

     

    c.f.) JPA2.1 부터는 스키마 자동 생성기능을 자바 표준(java.persistance.schema-generation.database.action)으로 제공한다. 하지만 하이버네이트가 제공하는 update, validate 옵션을 지원하지 않는다.

     


     

    제약조건 추가하기

     

    DB에 테이블을 생성하다보면 칼럼별로 제약 조건이 필요하다. 예를 들어 Member 테이블에 name 칼럼은 null이면 안되고, 길이가 10 이하여야 한다고 가정하자 그렇다면 다음과 같이 DDL이 작성되어야 할것이다.

    	NAME varchar(10) NOT NULL

     

    JPA에서는 스키마 자동 생성 기능을 이용해 칼럼의 제약조건을 추가하려면 @Column 애노테이션에 제약 조건과 관련된 속성을 추가해주면 된다.

     

    @Entity
    @Table(name = "MEMBER")
    public class Member {
    	
        @Id
        @Column(name = "ID")
        public Long id;
        
        @Column(name = "NAME", nullable = false, length = 10)
        public String name;
    
    	...
        
    }

    nullable 속성을 false로 설정하여 null 값을 제한하고, length 속성을 통해 길이를 제한해주었다. 출력된 DDL을 보면

    Hibernate: 
        create table member (
            id bigint not null,
            age integer not null,
            birth timestamp,
            name varchar(10) not null,
            primary key (id)
        )

    name 칼럼에 길이와 null값 제한이 생긴 것을 볼 수 있다.

     

    유니크 제약조건을 만들고 싶을 경우, @Table 어노테이션에 uniqueConstraints 속성을 추가해주어야 한다.

    Member 테이블에 {name, age}를 유니크하게 만들어 보자.

    @Entity
    @Table(name = "MEMBER", uniqueConstraints = {@UnigueConstraints(
        name = "NAME_AGE_UNIQUE",
        columnNames= {"NAME", "AGE"}
    )})
    public class Member {
    	
        @Id
        @Column(name = "ID")
        public Long id;
        
        @Column(name = "NAME", nullable = false, length = 10)
        public String name;
    
    	@Column(name = "AGE")
        public int age;
        
        ...
        
    }

    @UniqueConstraints 어노테이션을 지정하고, 유니크 키의 이름과 유니크 제약 조건을 걸 칼럼 그룹을 설정해주면 된다. 

     

    어플리케이션을 실행하면 다음과 같은 DDL이 출력된다.

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

     

    이 기능을 사용하면 개발자는 쿼리나 DB를 직접 보지 않고도 쉽게 제약 조건을 파악할 수 있는 장점이 있다.

     


    마무리

    이번 포스팅에서는 객체-엔티티, 엔티티-테이블 매핑을 위해 필요한 어노테이션들을 살펴보고, JPA의 핵심 기능인 스키마 자동 생성 기능에 대해 알아보았다. JPA는 이러한 기능을 통해 개발자가 코드만 보고도 어떤 DB를 생성했는지 쉽게 파악할 수 있게 해준다.  

     

    다음 시간에는 엔티티 매핑에서 매우 중요한 파트인 기본 키(Primary Key) 매핑을 알아보도록 할 것이다.