FireDrago

[JPA] 엔티티 매핑 본문

프로그래밍/JPA

[JPA] 엔티티 매핑

화이용 2024. 3. 27. 15:02

JPA 에서 가장 중요한 두가지는 영속성 컨텍스트매핑이다.

이번시간에는 JPA에서 엔티티 객체와 데이터베이스를 어떻게 매핑 하는지 알아보자

 

 

객체와 테이블 매핑

@Entity
@Table(name = "MEMBER", uniqueConstraints = {
    @UniqueConstraint(name = "UK_USERNAME", columnNames = {"USERNAME"}),
    @UniqueConstraint(name = "UK_EMAIL", columnNames = {"EMAIL"})
})
public class Member {
    private Long id;
    
    // 기본 생성자는 필수   
    public Member(){}
}

JPA를 사용하기 위해서 클래스에 반드시 @Entity 애노테이션을 붙여줘야한다.

또한 기본생성자가 반드시 필요하다. 

 

@Table 어노테이션은 여러가지 속성을 사용할 수 있다.

속성 기능 설명
name 매핑할 테이블 명 기본값은 엔티티이름  
schema 데이터베이스 스키마 매핑  
uniqueConstraint DDL 생성시 유니크 제약조건 예시 코드의 USERNAME, EMAIL 컬럼은 테이블에서
유일한 값이어야 한다.

 

 

DB 스키마 자동생성

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="hello">
        <properties>
            <!-- 필수 속성 -->
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="sa"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <!-- 옵션 -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments"  value="true"/>
            <!-- 데이터베이스 스키마 자동생성 옵션 -->
            <property name="hibernate.hbm2ddl.auto" value="none" />
        </properties>
    </persistence-unit>
</persistence>
<!-- 데이터베이스 스키마 자동생성 옵션 -->
<property name="hibernate.hbm2ddl.auto" value="none" />

 

JPA는 애플리케이션 실행시 엔티티의 설정대로 DDL을 생성하는 기능이 있다.

DB 스키마를 자동생성 기능은 실제 운영서버에서는 사용할 수 없다.

애플리케이션이 실행될때마다 DB 데이터가 초기화 되기 때문이다. 

 

hibernate.hbm2ddl.auto 값을 통해 설정 할 수 있다.

옵션 설명
create 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)
create-drop create 와 같으나 종료시점에 테이블 DROP
validate 엔티티와 테이블이 정상 매핑되었는지만 확인 (테스트서버, 운영서버)
update 변경분만 반영 (테스트 서버)
none 사용하지 않음 (운영서버)

 

 

필드와 컬럼 매핑

@Entity
public class Member {

    @Id @GeneratedValue(strategy  = GenerationType.AUTO)
    private Long id;
    @Column(name = "name")
    private String username;
    private Integer age;
    @Enumerated(EnumType.STRING)
    private RoleType roleType;
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
    @Lob
    private String description;
    
    public Member() {}
}

 

@Column : 컬럼매핑

속성 설명 기본값
name 필드와 매핑할 테이블의 컬럼이름 객체의 필드이름
insertable,
updatable
등록, 변경 가능 여부 TRUE
nullable(DDL) DDL 생성시 not null 제약조건  
unique(DDL) @Table의 uniqueConstaints 와 동일  
columnDefinition
(DDL)
데이터베이스에 컬럼 정보를 직접 줄 수 있다.
ex) varchar(100) default 'EMPTY'
필드의 자바타입과
방언 정보 사용
length(DDL) String 타입에 길이 제약조건 varchar(숫자) 입력된다. 255
preicision,
scale(DDL)
BigDecimal 타입에서 사용
precision은 소수점 포함한 전체 자리수
scale은 소수의 자릿수
precision = 19,
scale = 2

 

@Enumerated : Enum 타입에 사용

EnumType.STRING : enum 이름을 데이터베이스에 저장

 

LocalDate, LocalDateTime : date, timestamp 와 매핑된다. 별도의 어노테이션 없어도 됨 

 

@Lob : BLOB, CLOB 타입과 매핑, 문자열이면 CLOB, 나머지 BLOB

 

@Transient : 매핑 안함 

 

 

기본키 매핑

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

 

PK 컬럼과 매핑할때는 @Id 어노테이션을 붙인다. JPA는 @Id 어노테이션이 붙은 필드를 바탕으로 기본키로 생성한다

엔티티 클래스에서 PK 컬럼을 지정하려면 반드시 @Id 어노테이션을 사용해야 한다

 

기본키 생성은 웬만하면 식별자 용 id를 생성하고, Long 타입으로 최대값 초과를 방지하자, 

 

@GeneratedValue 어노테이션은 기본키의 자동생성전략을 설정할 수 있다.

속성 설명
IDENTITY MySql 등에서 사용된다. 특징적으로 트랜잭션 커밋 시점이 아니라 em.persist() 시점에 
SQL을 실행한다. 왜냐하면 1차 캐시를 사용하기 위해서는 키값으로 pk 값이 필요한데,
MySql은 Insert 쿼리가 실행되어야 pk값을 생성하기 때문이다. INSERT 실행하고 식별자를
조회하여 1차 캐시에 등록한다.
SEQUENCE Oracle 에서 사용된다. 기본키 생성만을 위한  특수 데이터베이스 오브젝트를 생성한다. (시퀀스)
em.persist() 시점에 시퀀스에 다음 pk 값을 조회하여 가져온다. 1차 캐시 등록하고, 트랜잭션 커밋 시점에 INSERT 쿼리를 실행한다.
allocationSize 값을 50 정도로 설정하면 한번에 50개씩 pk 값을 조회하여
매 INSERT 마다 pk를 조회하지 않도록 성능최적화를 할 수 있다.
AUTO 사용하는 데이터베이스에 맞게 자동으로 JPA가 pk 값 전략을 설정한다.

 

@Entity
// 시퀀스 전략
@SequenceGenerator( 
 name = “MEMBER_SEQ_GENERATOR", 
 sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
 initialValue = 1, allocationSize = 1) // 1부터 1씩 증가하는 pk
public class Member { 

 @Id 
 @GeneratedValue(strategy = GenerationType.SEQUENCE, 
 generator = "MEMBER_SEQ_GENERATOR") 
 private Long id;

@SequenceGenerator  속성

속성 설명 기본값
name 식별자 생성기 이름 필수
sequenceName 뎅이터베이스에 등록되어 있는 시퀀스 이름 hibernate_sequence
initialValue DDL 생성시 시퀀스 처음 시작하는 수를 지정한다. 1
allocationSize 시퀀스 한 번 호출에 증가하는 수 (성능최적화에 사용) 50
catalo, schema 데이터베이스 catalog, schema 이름  

 

 

데이터 중심의 엔티티 설계의 문제

 -> 객체 타입을  사용하지 못하고, 관계있는 PK 값을 사용해야함

자바 코드에서 객체를 바로 사용하지 못하고, id 값으로 다시 조회해야함 

 

JPA는 이 문제를 해결하기 위한 연관관계 매핑 기능을 제공한다.

// 객체지향적이지만 이렇게 설계불가 -> DB 테이블이랑 안 맞기 때문
@Column(name = "MEMBER_ID")
private Member orderMember;

// 어쩔 수 없이 이렇게 설계해야 한다.
@Column(name = "MEMBER_ID")
private Long memberId;

... 실행코드 ....

Order order = em.find(id);
// 여기서 Long 타입 키 조회
Long memberId = order.getMemberId();
// 다시 DB에 조회해야 함 (객체지향적이지 못함)
Member orderMember = em.find(memberId);

'프로그래밍 > JPA' 카테고리의 다른 글

[JPA] @PersistenceContext 사용해야 하는 이유  (0) 2024.04.08
[JPA] JPQL 기본문법 2  (0) 2024.04.06
[JPA] JPQL 기본문법 1  (0) 2024.04.04
[JPA] 연관관계 관리  (0) 2024.04.01
[JPA] 영속성 관리  (0) 2024.03.26