JPA

JPA기본기 다지기_필드와 컬럼 매핑

JANNNNNN 2024. 3. 22. 13:27

데이터베이스 스키마 자동으로 생성하기

  • DDL을 애플리케이션 실행 시 자동으로 생성
  • 테이블 중심 -> 객체 중심
  • 이렇게 생성된 DDL은 개발 장비에서만 사용 (운영에서 **절대!!**하면 안됨)
    • 개발과정에서는 ORM프레임워크를 사용해 개발자가 DDL을 직접 작성하지 않아도 자동으로 스키마를 생성해 테스트를 하는 것은 괜찮지만,
    • 운영과정에서는 실제로 프로그램을 배포하고 실행하여 사용자가 서비스를 이용하는 것이기 때문에 자동으로 생성된 DDL을 사용하면 데이터베이스의 스키마가 변경되어 데이터 손실등 예기치 않은 문제가 발생할 수도 있기 때문. 

❓DDL : Data Definition Language의 약자로, DB 스키마를 정의, 수정하는데 사용되는 SQL 

     ex) CREATE, ALTER, DROP, COMMENT ...

hibernate.hbm2ddl.auto

  • create : DROP + CREATE
  • create-drop : DROP + CREATE + DROP
  • update : 변경분만 반영
  • validate : 엔티티와 테이블이 정상매핑 되었는지만 확인
  • none : 사용하지 않음

주의!!

운영 장비에선 절대 create, create-drop, update 사용하면 안됨!!!

  • 개발 초기 : create or update
  • 테스트 서버 : update or validate
  • 스테이징과 운영서버 : validate or none

    실습 1. 스키마 자동 생성

    1. persistence.xml에 다음 설정 추가2. 전에 작성한 코드 실행전에는 재실행하면 Exception이 발생했었지만, 지금은 DROP 후 CREATE을 해주기 때문에 Exception이 발생하지 않는 것과, CREATE TABLE ...의 SQL이 실행되는 것을 볼 수 있다.
    @Entity // JPA의 객체이다!
    public class Member {
    
        @Id // PK값이다!
        private Long id;
        private String name;
        // age 값 추가
        private int age;
        ....
    4. 다시 실행새로 생성되는 Member 테이블에 age 컬럼이 추가된 것을 볼 수 있다➡️자동으로 테이블이 생성되거나 하면, 데이터가 손실되거나 잘못될 위험이 있기 때문
  • ⚠️이런 부분이 실제 운영단계에선 위험하다 !!
  • 3. Member class에 age 필드를 추가해보자!
  • <property name="hibernate.hbm2ddl.auto" value="create"/>

@Column

  • 가장 많이 사용된다.
  • name : 필드와 매핑할 테이블의 컬럼 이름
  • insertable, updatatable : 읽기 전용
  • nullable : null허용여부 결정, DDL생성시 사용
  • unique : 유니크 제약조건, DDL생성시 사용

@Temporal

@Temporal(TemporalType.DATE)
private Date date; //날짜
@Temporal(TemporalType.TIME)
private Date time;//시간
@Temporal(TemporalType.TIMESTAMP)
private Date timestamp;//날짜와 시간

@Enumerated

  • 열거형 매핑
  • EnumType.ORDINAL : 순서를 저장(기본값)
  • EnumType.STRING: 열거형 이름을 그대로 저장, 가급적 이것을 사용!!
@Enumerated(EnumType.STRING)
private MemberType memberType;

@Lob

  • 컨텐츠의 길이가 너무 길 경우, 바이너리파일로 DB에 넣어야 하는데 이때 사용한다
  • CLOB, BLOB 매핑
  • CLOB : String, char[], java.sql.CLOB
  • BLOB : byte[], java.sql.BLOB
  • 그냥 @Lob 어노테이션을 String타입에 쓰면 CLOB이 되고, Byte타입에 쓰면 BLOB가 된다.
@Lob
private String lobString;

@Lob
private byte[] lobByte;

@Tranisent

  • 이 필드는 매핑하지 않는다는 뜻이다
  • 애플리케이션에더 DB에 저장하지 않는 필드
  • 거의 사용하지 않는다.

식별자 Annotation

  • @Id
  • @GenerateValue
    • 옵션
      • AUTO : IDENTITY, SEQUENCE, TABLE 중 Dialect에 맞는 방식으로 자동 적용
        • ex) Id값을 넣지 않아도 자동으로 할당해줌
      • IDENTITY : Database에 "값을 알아서 채워줘"라는 뜻의 위임 (MySQL)
      • SEQUENCE : Sequence Object 사용 (Oracle)
      • TABLE : 키 생성용 테이블 사용 (모든 DB)
    • MySQL의 AutoIncrement, Oracle의 Sequence와 같은 느낌

권장하는 식별자 (=PK) 전략

  • 기본키 제약조건
    • Not Null
    • Unique
    • 불변
  • 미래까지 이 조건을 만족하는 자연키는 찾기 힘듦
    • 대체키를 사용하자!
    • 예를 들어 주민등록번호도 기본키로 사용하기 어려움!
  • 권장 : Long + 대체키 + 키 생성전략 사용
    • Int type ㄴㄴ! Long type ㄱㄱ!
    • or UUID 사용! -> But, 성능 문제는 확인해보고 사용

실습 2. 여러 Annotation 사용해보기

1. 위에 작성한 여러 Annotation들을 사용해보자

// 수정한 Member class
import javax.persistence.*;
import java.util.Date;

@Entity // JPA의 객체이다!
public class Member {

    @Id // PK값이다!
    @GeneratedValue(strategy = GenerationType.AUTO) // 키 자동 생성
    private Long id;

    @Column(length = 20)
    private String name;

    private int age;

    @Temporal(TemporalType.TIMESTAMP)
    private Date regDate;

    // 현업에선 무조건 STRING으로 써야 함
    // ORDINAL 썼을 경우 인덱스가 들어감 (0, 1, ..)
    // 기본값은 ORDINAL
    @Enumerated(EnumType.STRING)
    private MemberType memberType;

    ...
    // getter/setter
}
// 수정한 Main Class & method
package hellojpa;

import hellojpa.entity.Member;
import hellojpa.entity.MemberType;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        // 1. EntityManagerFactory 생성
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        // 2. EntityManagerFactory를 통해 EntityManager 생성
        EntityManager em = emf.createEntityManager();
        // 3. EntityManager를 통해 트랜잭션 획득
        EntityTransaction tx = em.getTransaction();

        // 4. 트랜잭션 시작
        tx.begin();

        // 에러 났을 경우를 대비해 try-catch로 처리
        try {
            // 5. 새로 삽입할 Member 객체 생성
            Member member = new Member();
            member.setId(1L);
            member.setName("안녕하세요");
            member.setRegDate(new Date());
            member.setMemberType(MemberType.USER);

            // 6.EntityManager를 통해 생성한 Member 객체 저장
            em.persist(member);

            // 7. 트랜잭션 커밋
            tx.commit();
        } catch (Exception e) {
            // 8. 실패하면 롤백!
            tx.rollback();
        } finally {
            // 9. EntityManagerFactory 및 EntityManager 종료
            em.close();
            emf.close();
        }
    }
}

실행해보자