JPA(Java Persistence API) 프로그래밍 - 엔티티 매핑

u-nij·2022년 6월 20일
0

JPA 프로그래밍

목록 보기
3/10
post-thumbnail

이 글은 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 듣고 정리한 글입니다.

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

객체와 테이블 매핑

@Entity

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

주의!

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

속성

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

@Table

  • 엔티티와 매핑할 테이블 지정

속성

  • name: 매핑할 테이블 이름, 기본값은 엔티티 이름을 사용
  • catalog: 데이터베이스 catalog 매핑
  • schema: 데이터베이스 schema 매핑
  • uniqueConstraints(DDL): DDL 생성 시에 유니크 제약 조건 생성

DDL(Data Definition Language)
: 데이터 정의어
데이터베이스를 정의하는 언어. 데이터를 생성, 수정, 삭제하는 등의 데이터 전체의 골격을 결정하는 역할을 하는 언어.
(ex. CREATE, ALTER, DROP, TRUNCATE, ...)

DML(Data Manipulation Language)
: 데이터 조작어. 정의된 데이터베이스에 입력된 레코드를 조회, 수정, 삭제하는 등의 역할을 하는 언어. 저장된 데이터를 실질적으로 처리하는데 사용한다.
(ex. SELECT, INSERT, UPDATE, DELETE, ...)

DCL(Data Control Language)
: 데이터베이스에 접근하거나 객체에 권한을 주는 등의 역할을 하는 언어.
(ex. GRANT, ROVOKE, COMMIT, ROLLBACK, ...)

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

  • JPA는 DDL을 애플리케이션 실행 시점에 자동 생성
  • 테이블 중심 → 객체 중심
  • 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성

    ex) H2 DB

    ex) Oracle DB

  • 이렇게 생성된 DDL은 개발 장비에서만 사용
  • 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용

persistence.xml

<property name="hibernate.hbm2ddl.auto" value="create" />
  • create: 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)
  • create-drop: create와 같으나, 종료 시점에 테이블 DROP
  • update: 변경분만 반영 (칼럼 추가만 가능, 지우는 것은 반영 X) (운영 DB에는 사용하면 안됨)
  • validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
  • none: 사용하지 않음

주의!

  • 운영 장비에는 절대 create, create-drop, update 사용하면 안된다.
  • 개발 초기 단계에는 create 또는 update (local 환경)
  • 테스트 서버에는 update 또는 validate (create, create-drop을 쓰면 데이터가 날라감)
  • 스테이징과 운영 서버는 validate 또는 none

    가급적이면 쓰지 않을 것을 권함. 테스트 서버와 스테이징에서 validate정도는 괜찮은 것 같음. 운영같은 경우에 alter 쿼리가 잘못 날라가면 시스템이 중단 상태가 될 수 있기 때문에, 로컬 환경에서만 사용하고, 테스트 서버나 운영 서버에서는 스크립트를 직접 짜는 것을 권함.

DDL 생성 기능

  • 제약 조건 추가
    ex) 회원 이름은 필수, 10자 초과 X : @Column(nullable = false, length = 10)
  • 유니크 제약 조건 추가
    ex) @Table(uniqueConstraints = {@UniqueConstraint( name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"} )})
  • DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고(DB에만 영향을 줌), JPA의 실행 로직에는 영향을 주지 않는다.

필드와 칼럼 매핑

실습

@Entity
public class Member {

    @Id
    private Long id;

    @Column(name = "name") // DB의 Column명은 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;

		@Transient
		private int temp; 

    public Member() {}

		// Getter, Setter, ...

}

결과

매핑 어노테이션 정리

  • @Column : 컬럼 매핑

    속성설명기본값
    name필드와 매핑할 테이블의 칼럼 이름객체의 필드 이름
    insertable, updatable등록, 변경 가능 여부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은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수다. 참고로 double, float 타입에는 적용되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다.
    recision=19, scale=2                               
  • @Temporal : 날짜 타입(java.util.Date, java.util.Calender) 매핑

    참고: LocalDate, LocalDateTime을 사용할 때는 생략 가능

      @Temporal(TemporalType.TIMESTAMP)
      private Date createdDate;
    
      @Temporal(TemporalType.TIMESTAMP)
      private Date lastModifiedDate;
    
      private LocalDate testLocalDate;
    
      private LocalDateTime testLocalDateTime;

    @TemporalType: DATE(날짜), TIME(시간), TIMESTAMP(날짜+시간)

    자바에서는 Date 데이터타입에 날짜, 시간이 포함되어 있지만, DB에는 3가지로 구분되어 있기 때문에 매핑 정보가 필요함.

  • @Enumerated : enum 타입 매핑 (DB에는 Enum 타입이 없음)

    속성설명기본값
    value- EnumType.ORDINAL: enum 순서를 데이터베이스에 저장
    - EnumType.STRING: enum 이름을 데이터베이스에 저장
    EnumType.ORDINAL

    주의! ORDINAL 사용 X

    기본적으로 Integer 타입으로 생성됨

    -> RoleType에 타입이 추가됐을 경우,

    => EnumType.STRING 필수!

  • @Lob : CLOB(문자), BLOB(문자 외) 매핑, varchar을 넘어서는 큰 컨텐츠

    • 지정할 수 있는 속성이 없다.
  • @Transient : 틀정 필드를 칼럼에 매핑하지 않음 (매핑 무시)

기본 키 매핑

어노테이션

  • @Id
  • @GeneratedValue

매핑 방법

  • 직접 할당 : @Id만 사용
  • 자동 생성 : @GeneratedValue
    • 속성 : strategy

strategy

  • IDENTITY : 기본 키 생성을 데이터베이스에 위임

    • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용

          @Entity 
          public class Member {
      
              @Id 
            @GeneratedValue(strategy = GenerationType.IDENTITY) 
            private Long id;
      
              //...
          }

      JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행한다.
      AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있다. → IDENTITY 전략em.persist() 시점에 즉시 INSERT SQL 실행한다. → JPA가 DB에서 식별자를 조회한 후, 영속성 컨텍스트의 PK 값으로 쓰게 됨.
      ⇒ 즉, 모아서 INSERT하는것이 불가능 (일반적으로 성능에 크게 차이가 있지 않음)

                  // code
                  Member member = new Member();
                  member.setUsername("C");
      
                  System.out.println("============================");
                  em.persist(member); // 즉시 INSERT SQL 실행
                  System.out.println("member.id = " + member.getId());
                  System.out.println("============================");
      
                  tx.commit();

  • SEQUENCE : 데이터베이스 시퀀스 오브젝트(유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트) 사용

    • 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
    • @SequenceGenerator 필요
      속성설명기본값
      name식별자 생성기 이름필수
      sequenceName             데이터베이스에 등록되어 있는 시퀀스 이름hibernate_sequence                 
      initialValueDDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 1 시작하는 수를 지정한다.1
      allocationSize시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용됨)
      데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다
      50
      catalog, schema데이터베이스 catalog, schema 이름

DB에서 시퀀스 값을 가져온 후, member의 ID에 값을 넣어주고 영속성 컨텍스트에 저장한다. 실제 트랜잭션을 commit()하는 시점에 INSERT 쿼리를 호출하게 된다.


성능문제? ⇒ allocationSize 설정

: 미리 50개를 증가시켜놓고, 웹 서버에서 50개를 씀.
ex) allocationSize = 50

			// code
			Member member1 = new Member();
            member1.setUsername("A");
            Member member2 = new Member();
            member2.setUsername("B");
            Member member3 = new Member();
            member3.setUsername("C");

            System.out.println("============================");

                                    // DB MEMBER_SEQ : -49
            em.persist(member1);    // DB MEMBER_SEQ : 1->51(2번 호출)  |  APP 1
            em.persist(member2);    // APP 2 (DB 부르지 않고, MEM에서 호출)
            em.persist(member3);    // APP 3 (DB 부르지 않고, MEM에서 호출)

            System.out.println("member.id = " + member1.getId());
            System.out.println("member.id = " + member2.getId());
            System.out.println("member.id = " + member3.getId());

            System.out.println("============================");

  • TABLE : 키 생성용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내, 모든 DB에서 사용 가능하지만 성능 문제가 있다.

    • @TableGenerator 필요

      속성설명기본값
      name식별자 생성기 이름필수
      table키생성 테이블명hibernate_sequence    
      pkColumnName시퀀스 컬럼명sequence_name
      valueColumnName시퀀스 값 컬럼명next_val
      pkColumnValue키로 사용할 값 이름엔티티 이름
      initialValue초기 값, 마지막으로 생성된 값이 기준이다1
      allocationSize시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용됨)50
      catalog, schema데이터베이스 catalog, schema 이름
      uniqueConstraints(DDL)     유니크 제약 조건을 지정할 수 있다.
      @Entity 
      @TableGenerator( 
              name = "MEMBER_SEQ_GENERATOR", 
              table = "MY_SEQUENCES", 
              pkColumnValue = "MEMBER_SEQ", allocationSize = 1)
      public class Member { 
      
              @Id 
              @GeneratedValue(strategy = GenerationType.TABLE,
                                              generator = "MEMBER_SEQ_GENERATOR") 
              private Long id;
      
              // ...
      
      }

  • AUTO: 방언에 따라 자동 지정, 기본값

권장하는 식별자 전략

  • 기본 키(pk) 제약 조건 : null 아님, 유일, 변하면 안된다.
  • 미래까지 이 조건을 만족하는 자연키는 찾기 어렵기 때문에, 대리키(대체키)를 사용하자.
  • 권장: Long형, 대체키, 키 생성전략 사용
    (AUTO_INCREMENT, SEQUENCE 추천. 비즈니스를 키로 끌고 오는 것은 바람직하지 않다.)
profile
삶은 달걀이다

0개의 댓글