본문 바로가기

Dev/JPA

엔티티 매핑

이번 글에서는 실제 JPA를 사용해서 테이블과 매핑하는 방법에 대해서 적어볼게요..


엔티티 매핑 종류

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

@Entity

 - @Entity가 붙은 클래스는 JPA가 관리, 엔티티라 한다.

 - JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수

+ 속성 : name

  • JPA에서 사용할 엔티티 이름을 지정
  • 기본값 : 클래스 이름을 그대로 사용(예 : Member)
  • 같은 클래스 이름이 없으면 가급적 기본값을 사용한다.

@Table

 - @Table은 엔티티와 매핑할 테이블 지정

+ 속성 : name - 매핑할 테이블 이름

+ 속성 : catalog - 데이터베이스 catalog 매핑

+ uniqueConstraints(DDL) -DDL 생성 시에 유니크 제약 조건 생성

 

주의사항

  • 기본 생성자 필수(파라미터가 없는 Public 또는 Protected 생성자)
  • final 클래스, enum, interface, inner 클래스 사용X
  • 저장할 필드에 final 사용 X

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

  • DDL을 애플리케이션 실행 시점에 자동 생성
  • 테이블 중심 -> 객체 중심
  • 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
  • 이렇게 생성된 DDL은 개발 장비에서만 사용
  • 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용

hibernate.hbm2ddl.auto 옵션 사용

  • create : 기존 테이블 삭제 후 다시 생성(DROP + CREATE)
  • create-drop : create와 같으나 종료시점에 테이블 DROP
  • update : 변경분만 반영(운영DB에는 사용하면 안됨) / 지우는것은 안됨
  • validate : 엔티티와 테이블이 정상 매핑되었는지만 확인
  • none : 사용하지 않음

데이터 베이스 스키마 자동 생성시 주의할점!!

  • 운영 장비에는 절대로 create, create-drop, update를 사용하면 안된다.
  • 개발 초기 단계는 create 또는 update
  • 테스트 서버는 update 또는 validate
  • 스테이징과 운영서버는 validate 또는 none 사용
 <property name="hibernate.hbm2ddl.auto" value="create" /> 
 <!-- 데이터베이스 스키마 자동 생성 속성으로 운영서버에서는 사용하지 말 것! -->
 <!--
   create 기존테이블 삭제 후 다시 생성
   create-drop create와 같으나 종료시점에 다시 drop (테스트 할 경우에 보통 사용)
   update 변경분만 반영(운영DB에는 사용하면 안됨)
   validate 엔티티와 테이블이 정상 매핑되었는지만 확인
   none 사용하지 않음

   운영 장비에는 절대 create, create-drop, update를 사용하면 안된다.
   개발 초기 단계에는 create 또는 update
   테스트 서버는 update 또는 validate
   스테이징과 운영 서버는 validate 또는 none
-->

필드와 컬럼 매핑

  • 대게 컬럼 매핑하는 예시는 아래와 같으며 어떻게 매핑할건지는 설정해주는 거에 따라 다르다.
 @Entity
 public class Member {

     @Id // 기본키 매핑
     private Long id;

     @Column(name = "name") // 컬럼명이 username이지만 실제 디비에는 name과 연결하겠다라는 뜻
     private String username;
     private Integer age;

     @Enumerated(EnumType.STRING) 
     // Enum 타입일 경우 사용하는데 ORDINAL은 사용하지 말자
     // ORDINAL은 순서를 디비에 저장하기때문에 예외사항이 발생할 수 있다.
     // Enum 타입은 그냥 아무생각말고 STRING을 사용하자!!!
     private RoleType roleType;

     @Temporal(TemporalType.TIMESTAMP) // 날짜형 타입
     private Date createdDate;

     @Temporal(TemporalType.TIMESTAMP)
     private Date lastModifiedDate;
     
     // 자바8 이후로는 하이버네이트에서는 LocalDate, LocalDateTime을 구분을 알아서 해준다. 그냥 쓰자
     // 옛날 버전인경우는 위와같이 @Temporal로 사용한다.
     private LocalDate testLocalDate;
     private LocalDateTime testLocalDateTime;

     @Lob // blob, clob 등의 데이터를 선언
     private String description;
}

대표적인 매핑 어노테이션

@Column 타입의 속성


기본 키 매핑 방법

- 직접 할당 : @Id만 사용

- 자동 생성(@GeneratedValue)

  • IDENTITY : 데이터베이스에 위임, MYSQL
  • SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용, ORACLE → @SequenceGenerator 필요
  • TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용 → @TableGenerator 필요
  • AUTO : 방언에 따라 자동 지정, 기본 값

IDENTITY 전략 - 특징

  • 기본 키 생성을 데이터베이스에 위임
  • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
  • JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행
  • AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있음
  • IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행 하고 DB에서 식별자를 조회
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    // IDENTITY로 하게되면 디비에 값이 들어가봐야 PK가 뭔지 알기떄문에 제약이 있음
    private Long id;
try{

  Member member = new Member();
  member.setUsername("C");

  System.out.println("===========");
  em.persist(member);
  System.out.println("member.id = " + member.getId());
  System.out.println("===========");

  //원래는 커밋하는 시점에 쿼리가 날라가야하는데 그러게되면 PK값을 알 수 없다.
  //IDENTITY 일 경우에만 예외적으로 em.persist 함과 동시에 디비로 값이 날라가서 ID(PK)값을 알 수 있게됨
  tx.commit();

IDENTITY 일 경우 commit 하기전에 디비로 날라감(예외)

SEQUENCE 전략 - 특징

  • 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트(오라클 시퀀스)
  • 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
try{
  // 1. JPA는 우선 member 객체를 만든다.
  Member member = new Member();
  member.setUsername("C");

  System.out.println("===========");
  
  // 2. em.persist 할때 영속성 컨텍스트에 넣어야하는데 그럴려면 PK가 필요함 ...
  // 그렇게되면 먼저 시퀀스를 가져와야한다!
  
  // 5. 값을 가져와서 저장한 후에 영속성 컨텍스트에 저장한다.
  // 6. 디비에 아직 INSERT 쿼리는 안날라가고 (필요에 따라 버퍼링을 해야하니) 영속성 컨텍스트에 쌓아둔다.
  em.persist(member);
  
  System.out.println("member.id = " + member.getId());
  System.out.println("===========");

  // 7. 커밋 하는 시점에 쿼리가 디비로 날라간다. (즉 SEQUENCE 방식은 버퍼링이 가능하다라는 뜻)
  tx.commit();
@Entity
// 3. JPA는 어? 너 시퀀스 전략이네! 란걸 확인하고 값을 먼저 가져올게 하고 동작하여 값을 가져오게된다.
@SequenceGenerator(
	name = “MEMBER_SEQ_GENERATOR",
	sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
	initialValue = 1, allocationSize = 1)

public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE,
    		generator = "MEMBER_SEQ_GENERATOR")
    // 4. 시퀀스 전략인걸 확인하고 나서 JPA는 가져온 값을 member에 id에 값을 넣어준다.
	private Long id;

3. JPA가 call next value for MEMBER_SEQ로 값을 미리 가져옴

@SequenceGenerator 전략

  • initialValue 옵션과 allocationSize 옵션을 통해서 성능을 최적화한다라는 점만 기억!

TABLE 전략

  • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
  • 운영에서는 아무래도 사용하기 꺼려진다. 성능이 안좋음

TABLE 전략 매핑
@TableGenerator 전략의 속성


권장하는 식별자 전략

  • 기본키 제약 조건 : null 아님, 유일, 변하면 안된다.
  • 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자
  • 예를 들어 주민등록번호도 기본 키로 적절하지 않다.
  • 권장 : Long형 + 대체키 + 키 생성전략 사용

'Dev > JPA' 카테고리의 다른 글

다양한 연관관계 매핑  (0) 2020.07.28
연관관계 매핑 기초  (0) 2020.07.15
JPA내부 구조(영속성 관리)  (0) 2020.07.06
JPA의 등장  (0) 2020.06.30
SQL중심개발의 문제점  (0) 2020.06.30