Spring/자바 ORM 표준 JPA 프로그래밍

[JPA 기본편] 4. 엔티티 매핑

kyung.Kh 2024. 5. 17. 02:14

객체와 테이블 매핑

@Entity

  • JPA를 사용해서 테이블과 매핑할 클래스는 @Entity가 필수이며 @Entity가 붙은 클래스는 JPA가 관리하며, 엔티티라 한다.
  • 기본 생성자가 필수로 있어야 함
  • final 클래스, enum, interface, inner 클래스에 사용할 수 없으며, 저장할 필드에 final을 사용하면 안된다.
  • @Table(name=" ")를 사용하여 테이블 이름을 정할 수 있다.

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

  • DDL을 애플리케이션 실행 시점에 자동으로 생성하며 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL을 생성한다.
    • 데이터베이스 방언별로 DDL이 달라짐 → Oracle로 실행하면 varchar2로 생성되지만 h2로 실행하면 varchar로 생성된다. 
  • 테이블 중심 → 객체 중심
  • 스테이징, 운영 서버 : validate, none
  • 개발 초기 : create, update
  • 테스트 서버(여러 명이 개발 중인 중간 서버) : update, validate
  • persistence.xml의 hibernate.hbm2ddl.auto의 value값 또는 application.yml의 spring:jpa:hibernate:ddl-auto의 값을 조정
옵션 설명
create 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)
create-drop create와 같으나 종료 시점에 테이블 DROP
update 변경분만 반영 (운영DB에서는 사용하면 안됨)
validate 엔티티와 테이블이 정상 매핑되었는지만 확인
none 사용하지 않음

DDL 생성 기능

@Column(unique = true, length = 10) 으로 제약 조건을 추가할 수 있으며, DDL 자동 생성할 때만 사용되고 JPA 실행 로직에는 영향을 주지 않는다. 즉, DB에만 영향을 준다.

반면, @Table(name = " ")은 insert 쿼리에 영향을 준다.


필드와 컬럼 매핑

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

    @Id
    private Long id;

    @Column(name = "name")  // DB에 표시되는 이름 → @Column : 컬럼 매핑
    private String username;

    private Integer age;  // Integer로 해도 알아서 디비 타입과 매핑

    @Enumerated(EnumType.STRING)  // @Enumberated : ENUM타입 매핑 시 
    private RoleType roleType;

    @Temporal(TemporalType.TIMESTAMP) //DB에 날짜관련 매핑 시
    private Date createdDate;

    @Temporal(TemporalType.TIMESTAMP) //DB에 날짜관련 매핑 시 → @Temporal : 날짜 타입 매핑
    private Date lastModifiedDate;

    @Lob  //varchar보다 큰 타입을 넣고 싶은 경우 → @LOB : BLOB, CLOB 매핑
    private String description;

    @Transient  // @Transient : 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)
    private int temp;  // DB에 넣지 않는 데이터
}

@Column

속성 설명 기본값
name 필드와 매핑할 테이블의 컬럼 이름 객체의 필드 이름
insertable, updatable 등록, 변경 가능 여부.
insert문이나 update문이 나갈 때, 이 컬럼의 변경 여부를 결정하는 것으로 DB에서 강제로 업데이트하지 않는한, updateable = false로 지정하면 절대로 반영되지 않는다.
TRUE
nullable(DDL) null 값의 허용 여부를 설정. false로 설정하면 DDL 생성 시에 NOT NULL 제약조건이 붙는다.  
unique(DDL) @Table의 uniqueConstraints와 같지만 한 컬럼에서 간단히 유니크 제약조건을 걸 때 사용.
한 컬럼에만 가능하고 이름이 알아보기 어려운 랜덤값이 되므로 잘 사용하지 않는다.
 
columnDefinition(DDL) 데이터베이스 컬럼 정보를 직접 줄 수 있다.
ex) varchar(100) default 'EMPTY'
필드의 자바 타입과 방언 정보를 사용함
length(DDL) 문자 길이의 제약 조건으로 String 타입에만 사용 255
precision, scale(DDL) BigDecimal 타입에서 사용(BigInteger도 사용 가능)
아주 큰 숫자나 소수점 사용 시에 사용
precision=19,
scale=2

@Enumerated

속성 설명 기본값
value - EnumType.ORDINAL : enum 순서를 데이터베이스에 저장
- EnumType.STRING : enum 이름을 데이터베이스에 저장
EnumType.ORDINAL
  • Enum 타입을 사용할 때는 STRING으로 저장해야 한다.
  • ORDINAL으로 생성 시, Integer 타입으로 생성된다. Enum 타입이 정수형으로 들어가게 되는데 항목이 변경, 추가되면 숫자가 꼬이게 되므로 절대로 ORDINAL을 사용하면 안된다!!

@Temporal

  • 날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용하며 과거 버전에서 사용했던 방식이다.
  • 최신 버전에서는 LocalDate, LocalDateTime을 주로 사용하며, 이걸 사용할 때는 @Temporal을 생략해도 된다.

@Lob

  • @Lob은 지정할 수 있는 속성이 없으며 매핑하는 필드 타입이 문자면 CLOB 매핑이 되고 나머지는 BLOB 매핑이 된다.

@Transient

  • 매핑을 하지 않을 때 사용하며, 데이터베이스에 저장과 조회를 하지 않는다.
  • 주로 메모리 상에서만 임시로 어떤 값을 보관하고 싶을 때 사용한다.

기본 키 매핑

  • 직접 할당 : @Id만 사용
  • 자동 생성 : @Id와 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택

권장하는 식별자 전략

  • 기본 키 제약 조건 : null 아님, 유일, 변하면 안됨
  • id는 Long형을 사용하는게 좋음. Integer(10억 제한)를 사용했다가 나중에 타입을 바꾸는 것보다 Long형을 처음부터 사용하는게 더 나음
  • 대체키를 사용해라. → 미래까지 기본 키 제약 조건을 만족하는 자연키를 찾기 어려우며 예를 들어 주민번호를 PK로 쓴다면, 다른 테이블의 FK도 다 주민번호로 설정해야 한다. 이러면 테이블마다 주민번호를 다 넣어주고 바꿔야되기 때문에 DB 마이그레이션할 때 불편하므로 비즈니스 관련 값을 사용하지 말고 랜덤 키값인 Long을 사용하는 것을 권장한다.
권장 : Long형 + 대체키 + 키 생성 전략 사용

기본 키 자동 생성 전략 - 4가지

IDENTITY 전략

기본 키 생성을 DB에 위임하는 전략으로 MySQL(AUTO_INCREMENT), PostgreSQL, SQL Server DB2에서 사용한다.

JPA는 보통 트랜잭션커밋 시점에 INSERT SQL을 실행하는데 이 전략은 DB에 먼저 INSERT SQL을 실행한 후에 기본 키를 알 수 있어서 em.persist() 시점에 즉시 INSERT SQL을 실행하고 DB에서 식별자를 조회하게 된다.

@Entity
public class Member {

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

 

위 코드와 같이 작성하여 사용하면 되며 아래의 결과를 보면 em.persist() 시점에 INSERT SQL이 실행된 것을 확인할 수 있다.

이 전략을 사용하면 Id 값이 자동으로 채워진다.

SEQUENCE 전략

유일한 값을 순서대로 생성하는 특별한 데이터베이스 오블젝트이다. 

여기에서 만든 시퀸스를 사용해서 기본키를 생성하며, Oracle, PostgreSQL, DB2, H2에서 시퀸드를 지원한다.

@SequenceGenerator를 사용해서 "MEMBER_SEQ_GENERATOR"라는 시퀸스 생성기를 등록했다.

그리고 sequenceName으로 "MEMBER_SEQ"로 지정했는데 이러면 JPA가 이 시퀸스 생성기를 DB의 "MEMBER_SEQ"와 매핑한다.

 

@GeneratedValue를 사용해서 SEQUENCE 자동 생성 전략으로 타입을 정하고, 위에서 등록한 "MEMBER_SEQ_GENERATOR"라는 시퀸스 생성기를 선택했다. 이러면 값이 추가될 때마다 id 식별자 값이 저 시퀸스 생성기에 하나씩 증가된다.

 

SEQUENCE 전략을 사용했을 때의 결과를 보면 IDENTITY 전략과는 다르게 persist() 시점에 시퀸스를 통해서 식별자를 조회하는 추가 작업(왼쪽 3번)이 필요하다. 시퀸스 전략은 persist()에 모아놨다가 commit()에 한 번에 보내는 것이 가능하다. 따라서 DB와 2번 통신하게 된다.

 

DB 시퀸스 생성 부분에 "create sequence MEMBER_SEQ start with 1 increment by 1" 이라는게 있는데 이건 1부터 시작해서 1씩 늘리겠다는 의미이다. 이렇게 사용하면 매번 통신할 때마다 DB에 두 번씩 접근하므로 비효율적이다. JPA는 시퀸스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator의 allocationSize를 사용한다. 기본값은 50이며 일반적으로는 50~100으로 설정한다고 하지만 성능이 중요하지 않으면 1로 설정해도 된다.(일단 스킵...)

TABLE 전략

키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀸스를 흉내내는 전략이다.

모든 데이터베이스에 적용이 가능하지만 성능이 나쁘다.

@TableGenerator로 테이블 키 생성기를 등록하고 생성한 MY_SEQUNCES 테이블을 키 생성용 테이블로 매핑했다. 그리고 @GeneratedValue의 generate 속성으로 "MEMBER_SEQ_GENERATOR" 테이블 키 생성기를 지정했다. 이러면 id 식별자 값은 "MEMBER_SEQ_GENERATOR" 테이블 키 생성기가 할당한다.

TABLE 전략은 값을 조회하면서 SELECT 사용 후 다음 값을 증가시키기 위해 UPDATE를 한 번 더 사용한다. 이는 시퀸스 전략보다 한 번 더 DB와 통신한다는 차이점이 있다.

 

 

[출처]

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard이 글은 김영한님의 JPA 강의 중 4장을 듣고 정리한 내용입니다

https://ssdragon.tistory.com/72#%EA%B-%B-%EB%B-%B-%ED%--%A-%--%EC%-E%--%EB%-F%--%--%EC%--%-D%EC%--%B-%--%EC%A-%--%EB%-E%B-%---%--SEQUENCE

728x90