[스프링 JPA] - 데이터베이스 기본 키 매핑과 전략

sonnng·2023년 9월 23일
0

Spring

목록 보기
10/41

1. 객체와 테이블 매핑 @Entity, @Table

  • @Entity : JPA가 관리하는 클래스이다. JPA를 사용해서 테이블과 매핑할 클래스는 @Entity가 필수이다.

  • 주의 : 기본 생성자 필수(파라미터가 없는 public/protected 생성자)

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

  • 애플리케이션 실행시점에 DDL을 자동으로 생성되도록 할 수 있다. 필요하면 테이블을 다 만들어준다. 데이터베이스 방언에 따라 그게 맞는 적절한 DDL을 생성된다.

  • 다만 생성된 DDL은 개발서버에서만 사용해야 하며, 운영서버에서는 적합하지 않다.

  • hibernate.hbm2ddl.auto = auto : 테이블이 있으면 DROP 명령어가 수행되고 이후에 다시 테이블을 새로 생성한다.

  • create : 기존 테이블 삭제 후 다시 생성(DROP+CREATE)

  • hibernate.hbm2ddl.auto = create-drop : create와 동일하나, 프로젝트 종료할 때 테이블을 삭제한다.(CREATE+DROP)

  • hibernate.hbm2ddl.auto = update : 변경분만 반영하며 운영 서버에서는 사용해서는 안된다.

  • hibernate.hbm2ddl.auto = validate : 엔티티와 테이블이 정상 매핑되었는지만 확인할 때 사용한다.

  • 방언에 따라서 데이터베이스에 맞게 스키마가 자동으로 생성된다.

    • 오라클의 경우, 문자열은 varchar2로 스키마가 생성됨을 확인할 수 있다.
  • 개발초기 단계 : create or update 권장

  • 테스트 서버 : update or validate 권장

  • 스테이징과 운영서버 : validate or none 권장

    • 운영서버는 alter로 잘못 고치면 굉장히 위험해진다. 가급적이면 테스트 서버로 잘 적용이 되는지 확인하고 운영서버에 적용하는 것이 권장된다. 운영서버에는 직접 쿼리문을 작성해서 작성한다.
    • alter table을 하게 되면 테이블 데이터가 lock되기 때문에 더욱 update나 create, create-drop을 사용해서는 안된다.
  • 운영장비에서는 절대 create, create-drop, update 사용해서는 안된다.

  • DDL 생성기능은 자동 생성할 때만 사용, JPA 실행 로직에는 전혀 영향을 주지 않는다.

3. 필드와 컬럼 매핑 @Column

  • 예시) 1. 회원은 일반 회원과 관리자로 구분

    2. 회원 가입일과 수정일이 있어야 한다.

    3. 회원을 설명할 수 있는 필드가 있어야 한다. 이 필드는 길이 제한이 없다.

  • [enum 타입 매핑] : 데이터베이스에는 이넘 타입이 없으나, 객체에 이넘타입을 사용하고 싶을 때, @Enumerated를 사용하면 된다.

  • [날짜 타입 매핑] : @Temporal

    • 데이터베이스에서는 날짜 타입이 세 가지가 있다.

      
      package javax.persistence;
      
      public enum TemporalType {
          DATE, //날짜만
          TIME,//시간만
          TIMESTAMP;//날짜와 시간 모두
      
          private TemporalType() {
          }
      }
      
  • @Column

    • name : 매핑할 테이블의 컬럼명
    • insertable : 수정되었을 때 update를 할 것인지 말것인지
    • nullable : 기본이 true, false로 해놓으면 not null 제약조건이 된다.
    • unique : true로 하게 되면 이름이 랜덤하게 못알아보게 생성되기 때문에 이름을 반영하기 어려워 잘 사용하지 않는다.
    • columnDefinition : 컬럼 정보를 직접 줄 수도 있다.
    • precison : 아주 큰 숫자나 소수점을 쓸 때 사용한다.
  • @Enumerated

    • EnumType.ORIGINAL : enum 순서를 데이터베이스에 저장한다.(default) 따라서 DB에는 숫자로 0부터 차례대로 들어간다.
      • 기본을 써서는 안된다! .. 이유는 Enum Type에 새로 이넘을 추가한 경우 DB에는 업데이트되어 바뀌지 않기 때문이다. 굉장히 위험하다.
    • EnumType.STRING : 필수로 써야하는 옵션이다. 이넘의 문자가 그대로 들어간다. 이렇게 쓰는게 맞는 것이고 별도의 데이터 타입이 들어가도 순서에 의한 오류를 예방할 수 있다.
  • @Temporal

    • 하이버네이트 최신버전을 사용한다면, LocalDate, LocalDateTime을 사용하면 된다.
    • LocalDate : 년월(DB에는 date타입으로 저장된다.)
      LocalDateTime : 년월일(DB에는 timestamp타입으로 저장된다.)
    • 과거버전을 사용한다면, TemporalType 을 사용한다.

      TemporalType.Date : 날짜, 데이터베이스 date 타입과 매핑

      TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑

      TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이스 timestamp 타입과 매핑
  • @Lob

    • 문자면 clob으로 매핑되고 나머지는 blob으로 매핑된다.
  • @Transient

    • 필드 매핑을 하고 싶지 않을 때 사용하며 데이터베이스에서 저장하거나 조회하지 않을 때 사용한다.

4. 기본 키 매핑 @Id

  • 기본키 매핑방법

    package jpabasic;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.EntityTransaction;
    import javax.persistence.Persistence;
    
    public class Main {
        public static void main(String[] args) {
    
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
            EntityManager em = emf.createEntityManager();
            EntityTransaction tx = em.getTransaction();
            //엔티티 매니저에서 수행하는 모든 로직은 트랜잭션 안에서 수행되어야 한다.
            tx.begin();
    
            try{
            Member member = new Member();
            member.setId("ID_A");
            member.setUsername("C");
    
                em.persist(member);
    
                tx.commit();
    
            }catch(Exception e){
                tx.rollback();
            }finally {
                em.close();
            }
            emf.close();
        }
    
    }

    직접 할당을 하려는 경우 : @Id만 사용

    자동 생성하는 경우 : @GeneratedValue를 사용하면 된다.

    • GenerationType에는 크게 세 가지다. SEQUENCE, IDENTITY, TABLE(, AUTO(이 셋 중 하나로 선택되는 것))

      //
      package javax.persistence;
      
      public enum GenerationType {
          TABLE,
          SEQUENCE,
          IDENTITY,
          AUTO;
      
          private GenerationType() {
          }
      }
      
    • IDENTITY전략 : DB가 기본 키 생성을 위임받는 것을 말한다.(주로 MySQL을 사용하는 경우 사용한다.)(크게 메리트는 없다.)

      • 기본 키는 db에 값이 들어간 후에야 키 값을 알 수 있다.
      • 이 전략만, 커밋되기 전에 INSERT 쿼리문이 실행되고 그 이후에 트랜잭션 커밋된다. 영속성 컨텍스트에 그 커밋된 값이 들어가게 된다. DB INSERT하는 시점에 그 상태값을 알고 있기 때문에 select문은 안나간다.
      • => em.persist() 가 되고 바로 INSERT 쿼리문 실행 -> 트랜잭션 커밋된다. 따라서 DB에 INSERT문이 들어간 후에야 ID값을 알 수 있다.
    • SEQUENCE전략 : 오라클 db에서 주로 사용한다. 시퀀스 오브젝트를 통해서 값을 생성, 이를 가져와서 객체에 기본 키를 세팅한다. 하이버네이트가 생성하는 기본 시퀀스를 사용한다.

      • 시퀀스 next val 을 가져와서 기본 키에 값을 주입, em.persist 가 이뤄진다.
      • SEQUENCEGenerate를 통해서 시퀀스명을 정의할 수 있다.
      • allocationSize : 데이터베이스에 한번 호출시 증가하는 수이다. 50으로 설정해둔다면, 1다음 바로 51이 된다.(성능 최적화에 사용)
    • (참고) : 기본 키는 Long을 사용하는 것으로 권장된다.

    • Table 전략 : 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략(현업에서는 잘 사용하지 않는다.)

      • TableGenerator를 통해서 사용할 수 있다.
  • 권장하는 식별자 전략

    • 기본 키 제약 조건을 생각해보자. (null이어서는 안되고 변해서는 안된다는 조건)
    • 미래까지 이를 만족하는 자연키는 찾기 어려우니까 대리키를 사용하는 것을 권장한다.
    • 권장 : Long형 + 대체키 + 키 생성전략(AUTO 또는 SEQUENCE) 사용
    • 비권장 : 비즈니스를 키로 끌고오는 것은 권장하지 않는다. (주민등록번호를 키로 사용하는 것)
  • 데이터 중심 설계 문제점

      @Column(name = "MEMBER_ID")
    private Long memberId; // 관계형 DB에 맞춰서 설계

    참조가 없어서 객체 그래프 탐색이 불가능하고 테이블 설계에 맞춘 방식이다.

0개의 댓글