Kangjieun11 2023. 1. 2. 02:41
728x90

 

 

 

자바 ORM 표준 JPA 프로그래밍

김영한


 

 


 

엔티티 매핑

 

 

목차

  • @Entity
  • @Table
  • 다양한 매핑 사용
  • 데이터베이스 스키마 자동 생성
  • DDL 생성 기능
  • 기본키 매핑
  • 필드와 컬럼 매핑 : 레퍼런스
  • 정리

 

실전 예제 요구사항 분석과 기본 매핑 

 


✅ @Entity

JPA를 사용해 테이블과 매핑할 클래스에 필수적으로 붙히는 어노테이션

@Entity를 붙히면 JPA가 관리하게 된다.

 

 

  • 기본 생성자가 필수적이다.
    • JPA는 엔티티 객체를 생성할 때 기본 생성자를 사용한다.
    • 생성자가 하나도 없는 경우는 에러가 안나지만(자바가 자동으로 만들어서), 
    • 어떤 생성자를 하나 이상 만들게 되면 직접 기본 생성자를 만들어줘야한다.
  • final, enum, interface, inner 클래스에는 사용불가
  • 저장할 필드에 final을 사용하면 안됨

 

https://stackoverflow.com/questions/3472438/why-cant-entity-class-in-jpa-be-final

 

Why can't entity class in JPA be final?

Why can't an entity class in JPA be final or have a final methods? Citing from here - An entity class must follow these requirements: ... The class must not be declared final. No metho...

stackoverflow.com

 

 

 

@Table

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

생략시 엔티티 이름을 테이블 이름으로 사용함.

유니크 제약조건을 만들어주는 속성인 uniqueConstraints만 따로 알고 가면 될것 같다.

catalog와 schema는 아직 입문자로서 알아야하나 싶다.

 

 

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

JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다.

클래스의 매핑 정보를 통해 어떤 테이블이 어떤 컬럼을 사용하는지 알수 있다.

 

application.yml

현재 진행중인 프로젝트에서 사용하는 속성인데

ddl-auto:create  = 어플리케이션 실행 시점에 DB table을 자동으로 생성한다.

show-sql : true   = 콘솔에 실행되는 DDL (데이터 정의어)를 출력할 수 있다.

 

jpa:
  hibernate:
    ddl-auto: create
  properties:
    hibernate:
      format_sql: true
  show-sql: true
  open-in-view: false

 

스키마 자동생성으로 만들어진 DDL은 운영환경에서 사용할만큼 완벽하지는 않다

따라서 개발환경에서 사용하거나, 매핑을 어떻게 해야하는지 참고하는 정도로만 사용하는것이 좋다.

 

> 객체, 테이블 매핑이 익숙하지 않을 경우 이기능을 적극활용하면 된다

생성된 DDL을 보고 엔티티, 테이블이 어떻게 매핑되는지 이해하기 쉽다.

 

⏺ ddl-auto의 속성

옵션 설명
create 기존 테이블을 삭제하고 새로 생성함 (Drop + Create)
create-drop create 속성에 추가로, 어플리케이션 종료할 떄 생성한 DDL을 제거 ( Drop + Create + Drop )
update DB 테이블과 엔티티 매핑정보를 비교해서 변경사항만 수정함
validate DB 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 어플리케이션 실행을 안함.  (이 설정은 DDL을 수정하지 않음)
none 자동생성 기능을 사용하지 않기 위해 속성자체를 안주거나, 유효하지 않은 옵션값(none)을 주면 된다.

 

* 운영서버에선 create, create-drop, update와 같이 DDL 수정 옵션은 사용하면 안된다. 

이 옵션들은 운영중인 DB의 테이블이나 컬럼이 삭제될 수 있기 때문이다.

 

  • 개발 초기 단계 - create / update
  • 초기화 상태로 자동화된 테스트 진행하는 개발자 환경, CI 서버 - create / create-drop
  • 테스트서버 - update / validate
  • 스테이징과 운영서버 - validate / none

 

 

✅ Unique 제약조건

Unique 제약조건을 추가하면, PK가 아닌 특정 열에 중복 값이 입력되지 않도록 할 수 있다.

 

 

✔️ 한개 컬럼에 UNIQUE 설정

@Column(name="column" , unique=true)
long column

 

✔️ 두개 컬럼을 묶어서 UNIQUE를 설정하면

@Table의 uniqueConstraints 사용

@Entity
@Table(
	name="tableName",
    uniqueConstraints={
        @UniqueConstraint(
            name={"contstraintName"}
            columnNames={"col1", "col2"}
        )
    }
)
@Data
public class Entity{
    @Column(name="col1")
    int col1;
    
    @Column(name="col2")
    int col2;
}

@Table의 uniqueConstraints 속성은 추가시 DDL 자동 생성할 떄만 사용되고,
JPA 실행 로직에는 영향을 주지 않는다. 

 

자동생성기능을 사용하지 않고, 직접 DDL을 만든다면 @Table의 uniqueConstraints를 사용할 이유가 없다 

 

 

기본키 매핑

 

영속성 컨텍스트는 Entity를 식별자 값으로 구분하기 때문에

엔티티를 영속 상태로 만들기 위해 식별자 값이 반드시 필요하다.

 

 

@GeneratedValue 

기본키 생성 전략을 결정한다.

 

  • 직접 할당 : 기본키를 Application에서 직접 할당한다.
    • @Id 를 사용해서, 엔티티 저장 전에 application에서 직접 할당 setId("id1")
  • 자동 생성 : 대리키 사용 방식이다.
    • 데이터베이스 벤더마다 기본키를 생성하는 방식이 달라서 자동생성전략이 여러가지 존재한다.
    • 오라클은 시퀀스를 제공한다.
    • MySQL은 AUTO_INCREMENT 기능을 제공한다.
      • IDENTITY 전략 -  기본키 생성을 DB에 위임한다.
        • 데이터를 DB에 INSERT한 이후에 기본키값을 조회할 수 있다.
        • 엔티티가 영속상태가 되려면 식별자가 반드시 필요하기 때문에 IDENTITY 전략을 사용할 경우, em.persist()가 호출되는 즉시 INSERT SQL이 DB로 전달되고, <트랜잭션을 지원하는 쓰기지연>은 동작하지 않는다. 
        • MySQL, PostgreSQL, SQL Server, DB2
      • SEQUENCE 전략 - Database 시퀀스를 사용해서 기본키를 할당한다
      • @SequenceGenerator 를 붙혀서 시퀀스 생성기 등록, sequence 
        • 식별자값을 획득한 후 영속성 컨텍스트에 저장한다.
        • em.persist() 호출시 먼저 DB 시퀀스를 사용해서 식별자를 조회하고, 조회한 식별자를 엔티티에 할당한 후에 영속성 컨텍스트에 저장한다. 
          이후 트랜잭션 commit해서 flush가 일어나면 엔티티를 DB에 저장한다. 
        • 오라클, PostgreSQL, DB2, H2
      • TABLE : 키 생성 테이블을 사용 
        • 키생성 전용 테이블을 하나 만들고, 이름과 값으로 사용할 컬럼을 만들어 시퀀스를 흉내내는 전략임
        • 테이블을 사용하므로 모든 데이터베이스에 적용 가능하다.
      • AUTO : 선택한 DB 방언에 따라 방식을 자동으로 선택해준다 (default)
        • 오라클 선택시 SEQUENCE, MySQL선택시 IDENTITY 사용됨
        • 디비를 변경해도 코드를 수정할 필요가 없는게 장점이다.
        • TABLE, SEQUENCE 전략 선택시를 대비해 미리 시퀀스나, 테이블을 만들어 놓아야한다.

 

✔️  @SequenceGenerator

* allocationSize의 defualt가 50인데, 시퀀스를 호출할떄마다 값이 50씩 증가한다. 

데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있다면 이 값을 반드시 1로 설정해야한다!!!

 

 

✅ 식별자 선택 전략

DB에서 사용할 기본키는 다음 3가지 조건을 모두 만족해야함

1. not null 

2. unique

3. 변하면 안됨

 

테이블의 기본키를 선택하는 전략은 2가지가 있는데

자연키 (natural key)  : 비지니스 적으로 의미가 있는 키 

>> 주민등록번호, 이메일, 전화번호

 

대리키 (surrogate key) : 비지니스와 관련없는 임의 키 (대체 키)

>> 오라클 시퀀스, auto_increment, 키생성 테이블 사용

 

 

 

* 자연키보다는 대리키를 권장함

(만약 전화번호를 기본키로 선택하면, 번호가 유일할수는 있다. 그러나 번호가 없는 경우도 존재하며, 번호가 변경될 수도 있기 때문에 기본키로 적당하지 않다. )

(주민번호는 기본키의 3가지 조건을 모두 만족하는 것 같지만, 언젠가 주민번호도 여러 이유로 변경될 수 있기때문에 적절하지 않다.)

 

* 비지니스 환경은 언젠가 변한다는것을 인지하자.

회원테이블에 주민등록번호가 기본키였던 적이 있었고, 그걸 외래키로 가져서 조인이 되어있었다.

어느날 정부 정책이 변하면서 주민등록번호를 저장할 수 없게 되었고, 엄청난 로직의 수정이 일어났다.

처음 설계했을 떄부터 대리키를 사용했다면 수정을 안해도 되었을텐데..!

 

* JPA는 모든 엔티티에 일관된 방식으로 대리키 사용을 권장

(비지니스 외부적인 풍파에 쉽게 흔들리지 않는 대리키가 일반적으로 좋은 선택이다)

 

* 기본키는 변하면 안되기 떄문에 setId()와 같이 식별자를 수정하는 메서드는 외부에 공개하지 않는것도 문제를 예방하는 하나의 방법이다.

 

 

필드와 컬럼 매핑 : 레퍼런스

 

@Column

필드를 테이블의 컬럼으로 매핑한다. 

속성 설명 defualt
name 필드와 매핑할 테이블 컬럼 이름   객체의 필드 이름
(DDL) nullable null 허용여부, false = not null (안전) true
(DDL) unique 한 컬럼에만 유니크 제약 조건 설정  
(DDL) columnDefinition 컬럼정보를 직접 설정 필드의 자바타입, 방언정보를 사용해서 적절한 컬럼타입 생성함.
(DDL) Length 문자 길이 제약조건 (String 에만 사용한다) 255
(DDL) percision ,  scale BigDemical, BigInteger에서 사용,
아주 큰 숫자 정밀한 소수를 다룰 때 사용한다. 
 percision 는 소수점을 포함한 전체 자릿수,
scale은 소수의 자릿수이다.

(double, float에 적용 x)
 percision = 19 ,  scale = 2
@Column(nullable = false)
private String data;
 
@Column(unique = true)
private String data;
 
@Column(columnDefinition = "varchar(100) default 'EMPTY'")
private String data;
 
@Column(length = 400)
private String data;
 
@Column(precision = 10, scale = 2)
private BigDecimal data;

 

@Column을 생략할 경우 대부분 기본값이 사용된다. 

그러나 int data; 와 같이 자바 기본타입에는 null이 입력할 수 없다.

 

따라서 JPA가 기본타입에는 NOTNULL제약조건을 추가해주고,  객체타입의 경우엔 NOT NULL을 설정하지 않는다.

 

 

 

@Enumerated

자바의 enum 타입 매핑시 사용

 

EnumType.ORIDINAL  :  enum에 정의된 순서대로 데이터베이스에 저장 됨 (순서가 바뀌는 경우 안전하지 않다)

EnumType.STRING  : enum 이름 그대로 데이터베이스에 저장됨 > ORDINAL에 비해 데이터 크기가 더 크게 저장되지만 안전하다. 

 

* defualt가 ORDINAL인데 STRING으로 꼭 바꿔주자

 

 

@Temporal

날짜타입 매핑시 사용 (java.util.Date, java.util.Calendar)

자바의 Date엔 년월일시분초가 존재하지만, DB에는 date, time, timestamp가 별도로 존재해서

@Temporal 생략시 Date와 가장 유사한 timestamp/datetime로 정의된다. 

 

mySQL : datetime

H2, Oracle, PostgreSQL : timestamp

 

  • @CreationTimestamp : INSERT 시 현재시간을 저장
  • @UpdateTimestamp : UPDATE 시 현재시간을 저장

 

@Lob

 

  • 데이터베이스 BLOB, CLOB 타입과 매핑
  • 매핑하는 필드 타입이 문자면 CLOB을 매핑하고 나머지는 BLOB을 매핑한다.
  • CLOB  :  String, char[]
  • BLOB  :  byte[]
@Lob
private String lobString;
 
@Lob
private byte[] lobByte;

 

@Transient

  • 필드에 매핑되지 않음 
  • DB에 저장, 조회 아무것도 안함
  • 객체에 임시로 어떤 값을 보관하고 싶을 떄 사용

 

@Access

  • JPA가 엔티티 데이터에 접근하는 방식을 지정
  • 필드 접근  :  AccessType.FIELD 로 지정
    • 필드에 직접 접근 (private도 접근 가능)
  • 프로퍼티 접근  :  AccessType.PROPERTY 로 지정
    • 접근자 Getter를 사용해 접근한다.

 

@Id가 필드에 있으므로 FIELD로 설정한것과 같다, -- @Access 생략해도 된다.

@Entity
@Access(AccessType.FIELD)
public class Member {
	@Id // id가 필드에 있으므로 @Access(AccessType.FIELD) 생략 가능
    private String id;
}

 

 

@Id가 프로퍼티에 있기 떄문에 PROPERTY로 설정한것과 같다 -- @Access 생략해도 된다.

@Entity
@Access(AccessType.PROPERTY)
public class Member {
    private String id;
    
    @Id // id가 프로퍼티에 있으므로 @Access(AccessType.PROPERTY)와 같다
    public String getId() {
    	return id;
    }
}

 

 

@Id를 필드에 넣어 필드 접근방식을 사용하고, getFullName()메서드만 프로퍼티 접근방식을 사용해도된다.

@Entity
public class Member {
	@Id // 기본은 필드 접근 방식
    private String id;
    
    @Transient
    private String firstName;
    
    @Transient
    private String lastName;
    
    @Access(AccessType.PROPERTY) // 이 메소드만 프로퍼티 접근 방식 
    public String getFullName {
    	return firstName + lastName;
    }
}

회원 엔티티를 저장하면 회원 테이블의 FULLNAME 컬럼에 firstName + lastName의 결과가 저장된다

 

 

 


 

 

 

나중에 볼것

 

https://www.inflearn.com/questions/387369/%EC%97%94%ED%8B%B0%ED%8B%B0%EB%A7%A4%EB%8B%88%EC%A0%80%EC%99%80-%EC%98%81%EC%86%8D%EC%84%B1%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%97%90-%EA%B4%80%ED%95%B4%EC%84%9C

 

엔티티매니저와 영속성컨텍스트에 관해서 - 인프런 | 질문 & 답변

여러 질문 답변을 찾아보고 내용을 종합해서 이해해본 결과  엔티티매니저와 영속성컨텍스트에 관해서 제가 현재 이해하고 있는게 맞는지 확인 부탁드립니다 ..   엔티티 매니저 1. 언제 생성

www.inflearn.com

 

 

출처

자바 ORM 표준 JPA 프로그래밍 - 김영한,

https://dev-coco.tistory.com/75

https://kudolove.tistory.com/1340