엔티티 매핑(1)

ANN·2024년 12월 27일

JPA(TIL)

목록 보기
4/4
post-thumbnail

해당 내용은 인프런 김영한 강사님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편'의 강의를 기반으로 작성했습니다.
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

📢 객체와 테이블 매핑

❓ @Entity

클래스에 @Entity를 붙이면, JPA가 관리하는 클래스가 됨
-> 엔티티라고 함

  • 이 애노테이션이 없으면, 그냥 내가 쓰고 싶은 JPA와 관련없는 클래스

⚠️ 주의

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

🤔 Why final클래스나 메서드가 문제가 되는가?

1. 프록시 클래스 생성 불가
Hibernate는 엔티티 클래스의 서브클래스를 만들어야 함
하지만 클래스에 final이 선언되어 있으면 상속이 불가능하므로 Hibernate가 프록시 객체 생성 불가

2. 프록시 메서드 오버라이딩 불가
HibernateLaze Loading 시 필요한 메서드를 오버라이드해야 함
하지만 메서드에 final이 선언되어 있으면 오버라이드할 수 없으므로 Lazy Loading이 미작동

이 부분은... 좀 더 공부해봐야 알 것 같다.

Entity 속성 정리

name

  • JPA에서 사용할 엔티티 이름 지정
  • 기본값 : 클래스 이름 그대로 사용
  • 같은 클래스 이름이 없으면 가급적 기본값 사용
  • 쓸 일은 거의 없는데(?), JPA가 내부적으로 그냥 구분하는 이름이며, 기본값을 써야 헷갈리지 않음

❓ @Table

JPA에서 엔티티 클래스를 데이터베이스의 테이블과 매핑하기 엔티티와 매핑할 테이블 지정

🤔 Entity의 name과 다를 게 뭐냐?

엔티티는 Member인데, 테이블은 규정상 축약을 사용하여 tb_mbr이라든지... 할 때 사용...?

아직 감이 덜 온다.

Table 속성 정리

name

  • 테이블 이름 지정
  • 지정하지 않으면 클래스 이름이 테이블 이름으로 생성
  • 기본값: 클래스 이름

catalog

@Entity
@Table(name = "users", schema = "public", catalog = "my_database")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
SELECT * FROM my_database.public.users;
  • 테이블이 속한 데이터베이스 카탈로그 지정
  • 특정 데이터베이스를 지정할 때 사용
  • 기본값: 빈 문자열

schema

@Entity
@Table(name = "users", schema = "public")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
SELECT * FROM public.users;
  • 테이블이 속한 스키마 지정
  • 기본값: 빈 문자열

    🤔 카탈로그와 스키마 설정 효과와 사용 시 주의사항

    1. 설정 효과
    • JPA가 쿼리를 실행할 때 테이블 경로를 명확히 정의 -> 동일한 이름의 테이블이 여러 스키마나 카탈로그에 있을 경우 충돌 방지
    • 다중 데이터베이스 또는 다중 스키마 환경에서 유용
    1. 사용 시 주의 사항
    • 데이터베이스 설정 확인
      • 데이터베이스 드라이버와 설정이 스키마 및 카탈로그 지원
    • 권한 문제
      • 사용 중인 데이터베이스 계정이, 설정된 스키마 및 카탈로그에 접근할 수 있는 권한이 있어야 함
    • MySQL 주의
      • MySQL에서는 스키마가 무시될 수 있으며, 대신 데이터베이스 이름(카탈로그) 사용
      • MySQL에서는 스키마와 카탈로그가 동일한 개념으로 취급

uniqueConstraints

@Table(uniqueConstraints = {
    @UniqueConstraint(columnNames = {"email"})
})
  • 테이블의 UNIQUE제약 조건 정의
  • 배열 형태로 여러 개의 제약 조건 설정
  • 기본값: 빈 배열

indexes

@Table(indexes = {
    @Index(name = "idx_user_email", columnList = "email")
})
  • 테이블에 대한 인덱스 정의
  • 기본값: 빈 배열

🤔 @Entity@Table의 차이?

@Entity와 해당 클래스가 JPA에서 관리되는 엔티티임을 선언
-> JPA가 해당 클래스를 데이터베이스 테이블과 매핑할 수 있도록 지정
@Table은 엔티티 클래스와 데이터베이스 테이블 간의 세부적인 매핑 설정
-> 데이터베이스 테이블의 이름, 스키마, 카탈로그, 인덱스, 유니크 제약 조건 등 지정

@Entity를 안 쓰고 @Table만 쓰면??

  • JPA에서 해당 클래스를 엔티티로 인식하지 않기 때문에, JPA를 통해 데이터베이스 테이블과의 매핑이나 쿼리 작업을 할 수 없음
  • CREATE 쿼리 자체도 나가지 않음

@Entity만 설정했을 때, JPA가 연결된 DBMS에 테이블이 생성되는 이유?

JPA가 제공하는 엔티티와 테이블 간의 매핑 기본 동작과 DDL 생성 전략에 기반

  1. 기본 동작 : JPA의 엔티티 관리
    @Entity 선언 시, JPA는 해당 클래스를 관리하게 되고,
    JPA구현체(ex. Hibernate)는 엔티티를 데이터베이스 테이블과 매핑하려고 시도
  2. 테이블 자동 생성 : DDL 생성 전략
    yml이나 properties파일에 DDL 생성 전략을 정의하는 설정 포함이 되어 있으면,
    애플리케이션 동작 시 JPA구현체는 엔티티 클래스 정보를 기반으로 테이블을 생성하는 SQL(DDL)을 자동으로 생성하고 실행

그러니까 왜?

  • JPA는 엔티티 클래스와 데이터베이스 테이블 간의 매핑을 제공하는 클래스
  • JPA는 @Entity가 선언된 클래스를 "데이터베이스에 저장되어야 하는 객체"로 간주
  • 따라서 JPA구현체는 해당 클래스의 정보를 바탕으로 테이블을 자동 생성

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

위에서 말했듯이 JPA가 매핑 정보만 보면 어떤 쿼리를 만들지, 어떤 테이블을 만들어야 할 지 알 수 있음.(로딩 시점에 DB의 테이블을 생성하는 기능을 지원)

  • DDL을 애플리케이션 실행 시점에 자동 생성
    = 테이블 중심 -> 객체 중심
    (장점: 개발할 때 테이블을 만들어놓고 객체로 개발할 필요 없음)
  • 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL을 생성
    • 오라클에선 VARCHAR2, MySQL에선 VARCHAR...

이렇게 생성된 DDL은 개발 환경에서만 사용하고, 운영 서버에서는 다듬어서 사용하거나 사용하지 말 것

❓ 속성

hibernate.hbm2ddl.auto

create

  • 기존 테이블이 존재하고 있으면 지우고, 새로 만듦(DB가서 ALTER할 필요가 없음)
  • DROP + CREATE

update

  • ALTER 쿼리를 날림

create-drop

  • create와 같음(있으면 DROP, CREATE로 새로 생성)
  • 애플리케이션 종료 시 DROP
  • 테스트케이스를 실행시키고, 마지막에 깔끔하게 다 날리고 싶을 때 쓰는 것

validate

  • 엔티티와 테이블이 정상적으로 매핑되었는지 확인
  • 이 설정을 하고, 새로운 컬럼 추가 시 -> DB의 테이블과 엔티티 클래스가 달라서 오류가 남

none

  • 사용하지 않겠다는 것
  • 명시적임

❓ 주의

  • 개발 초기 단계 : create 또는 update
  • 개발 어느정도 진행 : update 또는 validate
  • 스테이징이나 운영 서버 : validate 또는 none

하지만, 강사님 생각으로는,

  • 테스트 서버(여러 개발자가 함께 쓰는 서버)와 스테이징 : validate
  • 운영 서버 : none

❓ DDL 생성 기능

  • 제약 조건 추가 : 컬럼에 들어갈 데이터의 길이, 필수성 등
  • 유니크 제약조건 추가 등
  • DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않음
    • JPA로 인해 DDL 생성 기능과, JPA 실행 로직 자체는 독립적
    • JPA 실행 로직은 애플리케이션 코드와 데이터베이스의 구조에 의존하며, DDL 생성 기능의 결과와 무관하게 진행

📢 필드와 컬럼 매핑

❓ 요구사항이 다음과 같다면,

  1. 회원은 일반 회원과 관리자로 구분
  2. 회원 가입일과 수정일이 있어야 함
  3. 회원을 설명할 수 있는 필드가 있어야 함(이 필드는 길이 제한이 없음)
  • @Column(name = "name")
    • 엔티티 필드(username)은 애플리케이션 코드에서 사용
    • 컬럼 이름(name)은 데이터베이스 테이블에서 사용
  • private Integer age;
    • DB에서 Integer와 가까운 숫자 타입이 선택됨
  • @Enumerated(EnumType.STRING)
    • DB에는 enum이 없기 때문에 @Enumerated를 씀
  • @Temporal(TemporalType.TIMESTAMP)
    • 데이터베이스에 매핑 정보를 전달(추후 기술하겠지만, 자바 버전에 따라 안 해도 됨)
  • @Lob
    • 큰 컨텐츠가 들어감

enumvarchar랑 매핑됨
clob은 큰 문자열

❓ 매핑 어노테이션 정리

hibernate.hbm2ddl.auto

@Column

name

  • 필드와 매핑할 '테이블의 컬럼' 이름
  • 기본값: 객체의 필드 이름

insertable, updatable

  • 등록, 변경 가능 여부
  • 기본값: TRUE

컬럼을 수정했을 때 이 컬럼을 반영할 건지 아닌지(쿼리가 나갈 건지 아닌지)
FALSE로 해두면 절대 변경되지 않음

nullable(DDL)

  • null값의 허용 여부 설정
  • FALSE로 설정하면, DDL 생성 시 NOT NULL 제약 조건이 붙음

unique(DDL)

  • @TableuniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용
  • 이름을 반영하기가 어려움 → @Table에서 지정하는 게 나음

columnDefinition(DDL)

  • 데이터베이스 컬럼 정보를 직접 설정
  • 기본값: 필드의 자바 타입과 방언 정보 사용

length(DDL)

  • 문자 길이 제약조건, String 타입에만 사용
  • 기본값: 255

precision, scale(DDL)

  • BigDecimal 타입에서 사용(BigInteger도 사용 가능)
  • precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수
  • 참고로 double, float 타입에는 적용되지 않음
  • 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용
  • 기본값: precision = 19, scale = 2

@Enumerated

  • EnumType.ORDINAL: enum 순서를 데이터베이스에 저장
  • EnumType.STRING: enum 이름을 데이터베이스에 저장
  • 기본값: EnumType.ORDINAL

그러나 기본값인 ORDINAL로 쓰면 안 됨
enum 타입이 추가되었는데 기존 타입 앞에 놓이면, 순서가 바뀜
ex) 처음에는 ADMIN과 USER 만 이 순서로 타입이 지정되면 ADMIN = 0, USER = 1인데, GUESS가 ADMIN 앞에 쓰이면 GUESS = 0, ADMIN = 1, USER = 2가 되지만, 기존 레코드 데이터들은 바뀌지 않으므로 운영상 버그가 발생
-> STRING 타입을 사용할 것

@Temporal

  • 날짜 타입을 매핑할 때 사용
  • 지금은 사실 필요가 없음
  • 자바8에 LocalDate, LocalDateTime 사용 시 생략 가능

@Lob

  • 지정할 수 있는 타입이 없음
  • 필드 타입에 따라 매핑이 됨
    • 문자는 CLOB, 나머지는 BLOB 매핑

@Transient

  • 필드 매핑 X, 데이터베이스에 저장 X
  • 그냥 메모리에서만 쓸 경우 사용

0개의 댓글