2023-05-16 EFUB 7주차 세션을 듣고 정리한 내용입니다.
JPA란?
https://velog.io/@chhaewxn/%EC%9E%90%EB%B0%94-ORM-%ED%91%9C%EC%A4%80-JPA-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-JPA-%EC%86%8C%EA%B0%9C
앞의 포스팅 내용 참조 😊
JPA 엔티티 매핑
@Entity
- 생략할 수 없는 필수 어노테이션
- JPA는 @Entity가 설정된 클래스로부터 생성된 객체만 엔티티로 인지하고 사용할 수 있다.
➡️ @Entity를 일반 자바 객체와 구분하기 위한 어노테이션으로 이해해도 된다.
- 엔티티 클래스는 다른 엔티티 클래스와 구분하기 위한 유일한 엔티티 이름을 가지고 있어야 한다.
@Table
- 엔티티 이름과 테이블 이름이 일치하지 않는 경우 이 어노테이션을 이용하여 매핑할 테이블 이름을 지정해야 한다.
- 다양한 속성
- name
: 매핑할 테이블 이름을 지정
- catalog
: 데이터베이스 카탈로그를 지정
- schema
: 데이터베이스 스키마를 지정
- uniqueConstraints
: 결합 unique 제약 조건을 지정하며, 여러 개의 칼럼이 결합되어 유일성을 보장해야 하
는 경우 사용
@Column
- 엔티티 클래스의 멤버변수와 테이블 칼럼을 매핑할 때 사용
- 지원하는 속성은 다양하지만 name, nullable 정도를 주로 사용
- @Column(unique = true, length = 10) 테이블의 name 속성을 바꾸는 것은 런타임에 영향을 주지만, unique나 nullable 등의 제약 조건 설정은 JPA의 실행 로직에는 영향을 주지 않고 단순히 DDL을 생성하는 데에만 영향을 준다.
@Enumerate
- DB에는 enum 타입이 없지만, @Enumerated를 사용하면 된다.
- 자바 Enum 타입을 매핑할 때 사용한다.
- 속성으로는 ORDINAL과 STRING이 있다.
- ORDINAL(기본값) : enum 순서를 DB에 저장(Integer로 저장된다.)
- STRING : enum 이름을 DB에 저장
- ORDINAL을 사용하면 안된다!
USER랑 ADMIN만 가지고 있다가, 앞에 GUEST를 추가했다면?
기존에 0, 1이었던 USER와 ADMIN이 1, 2로 변경되는데,
기존의 데이터는 당시 저장된 숫자로 저장되어 있어 에러 발생
@Temporal
- 날짜 타입을 매핑할 때 사용
- 옛날에는 필요했지만 지금은 필요 없다
- 최신 하이버네이트에서는 그냥 LocalDate나 LocalDateTime을 사용
- LocalDate는 date, LocalDateTime는 timestamp로 DB에 각각 저장된다
- Date : 2013-10-11
- Time : 11:11:11
- Timestamp : 2013-10-11 11:11:11
@Lob
- 데이터베이스 BLOB, CLOB 타입과 매핑
- @Lob에는 지정할 수 있는 속성이 없다
- 매핑하는 필드 타입이 문자면 CLOB, 나머지는 BLOB으로 매핑
- CLOB : String, char[], java.sql.CLOB
- BLOB : byte[], javasql.BLOB
@Transient
- 필드 매핑 X
- 데이터베이스에 저장 X, 조회 X
- 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용
Table & Column Naming
예약어
: 주문을 나타내는 Order 의 경우 예약어를 피하기 위해 객체는 Order로 table은 Orders 로 나타내는게 관례이다.
Table 네이밍
: 소문자의 underscore (snake_case) 가 일반적이다.
Entity의 id
: 객체의 필드는 id 라고 만들고 @Column(name = "member_id") 이런 식으로 만드는 것이 좋다.
객체와 Table의 매핑
: Hibernate 의 기존 구현에서는 Entity의 필드명을 그대로 테이블의 필드명으로 적지만, springboot 사용 시 SpringPhysicalNamingStrategy 를 사용해서 필드명의 기본 전략이 camelCase ➡️ snakecase 이고, 대문자를 소문자로, '.' 을 '' 로 변경한다. (커스텀한 테이블 네이밍을 하고 싶은 경우에도 변경
해서 활용 가능하다
식별자 값 자동 생성
JPA는 엔티티 클래스로부터 생성된 엔티티를 기반으로 데이터 영속성을 관리한다.
따라서 엔티티 클래스를 데이터 베이스 테이블과 어떻게 매핑하느냐가 JPA의 핵심이자 전부다.
- 직접 할당: 기본 키를 애플리케이션에서 직접 할당 @Id
- 자동 생성: 대리 키 사용 방식
- IDENTITY 전략
- SEQUENCE 전략
- TABLE 전략
- 자동 전략
- 복합 키 사용하기 ➡️ 두 개 이상의 칼럼을 결합하여 키로 사용하는 방식. @Embeddable 가 붙은 식별자 전용 클래스 생성)
IDENTITY 전략
- 주로 MySQL이나 DB2같은 데이터베이스에서 사용하며 키 생성에 대한 처리를 전적으로 데이터베이스에게 위임
- 동작원리 : EntityManager의 persist 메소드가 호출되는 순간 Insert 구문이 생성되어 데이터베이스에 전송
- 데이터베이스에서 IDENTITY 전략으로 키를 생성한 후 엔티티 클래스의 식별자 변수에 할당함
- 데이터가 추가된 것을 확인하려면 transaction의 commit 메소드가 호출 된 후 확인 가능
SEQUENCE 전략
- 아이덴티티 전략과 사용방법은 거의 유사하지만 유일키를 생성하기 위해 시퀀스라는 별도의 오브젝트를 사용
하는 점에서 아이덴티티 전략과 차이가 있다. (오라클, PostgreSQL, DB2, H2에서 사용 가능)
TABLE 전략
- IDENTITY, SEQUENCE 전략은 데이터베이스에서 해당 기능을 지원해야 사용가능 하지만 JPA는 특정 데이
터베이스에 종속되지 않은 영속성 관리를 지향한다. ➡️ 데이터베이스에 종속되지 않은 전략이 필요
- 데이터베이스에 종속되지 않으며 별도의 식별자 테이블을 생성하여 식별값을 관리
자동 전략
- 데이터베이스가 확정되지 않은 상태에서 데이터베이스 연동 처리해야 하는 경우,
- 프로젝트 수행하는 도중에 데이터베이스가 변경되는 경우에 효율적
- 오라클을 선택하면 SEQUENCE를, MySQL을 선택하면 IDENTITY를 사용
연관관계 매핑
방향성
- 단방향: 회원과 팀이 있을 때 (개발자 -> 프로젝트), (프로젝트 -> 개발자) 둘 중 한쪽만 참조하는 것
- 양방향: (개발자 -> 프로젝트), (프로젝트 -> 개발자) 모두 서로 참조하는 것
매핑의 종류
- 일대일 @OneToOne
- 일대다 @OneToMany
- 다대일 @ManyToOne
- 다대다 @ManyToMany
양방향 매핑의 규칙: 연관관계의 주인
- 객체의 두 관계 중 하나를 연관관계의 주인으로 지정한다.
- 연관관계의 주인만이 외래 키를 관리(등록, 수정, 삭제)할 수 있다.
- 주인이 아닌 쪽은 읽기만 가능하다.
- 주인이 아니면 mappedBy 속성의 값으로 주인이 무엇인지 지정한다
누구를 주인으로 삼아야 하는가?
- 양방향은 외래 키가 있는 곳을 주인으로 정한다
- 여기서는 Member의 team이 연관관계의 주인이다
- DB입장에는, 외래키가 있는 곳이 다(N). 외래키가 없는 곳이 무조건 1이다
- 즉, Team의 members에 값을 넣고 변경해봤자 아무 일도 일어나지 않는다. 주인이 아닌 쪽은 읽기만 할 수 있다
- 비즈니스적으로 중요한 것이 주인이 되는 것이 아니라, DB 테이블로 따져서 N쪽인 곳이 주인이 되면 된다
예를 들어, 자동차와 자동차 바퀴에서는 자동차 바퀴가 N이며 연관관계의 주인이다
(1) 다대일 단방향 [N:1]
- 데이터베이스 테이블의 일(1), 다(N) 관계에서 외래 키는 항상 다쪽에 있다.
- 팀에는 회원을 참조하는 필드가 없다. 따라서 다대일 단방향 연관관계
(2) 다대일 양방향 [N:1, 1:N]
- Member의 team이 연관관계의 주인이다. JPA는 외래 키를 관리할 때 연관관계의 주인만 사용한다.
- 양방향 연관관계는 항상 서로를 참조해야 한다.
(3) 일대다 단방향 [1:N]
- 하나의 팀은 여러 회원을 참조할 수 있는데 이런 관계를 일대다 관계라 한다. 그리고 팀은 회원들을 참조하지만 회원은 팀을 참조하지 않으면 둘의 관계는 단방향이다.
- 일대다 관계에서는 반대편 테이블이 외래 키를 관리한다.
(4) 일대다 양방향 [N:1, 1:N]
- 일대다 양방향 매핑은 존재하지 않는다. 대신 다대일 양방향 매핑을 사용해야 한다.
- 양방향 매핑에서 @OneToMany는 연관관계의 주인이 될 수 없다. 관계형 데이터베이스의 특정 상 일대다, 다대일 관계는 항상 다 쪽에 외래 키가 있다. 따라서 @OneToMany, @ManyToOne 둘 중에 연관관계의 주인은 항상 다 쪽인 @ManyToOne을 사용한 곳이다
(5) 일대일 단방향 [1:1]
- ex) 회원과 사물함의 관계
- 회원과 사물함의 관계는 회원은 하나의 사물함만 사용하고 사물함도 하나의 회원에 의해 사용되므로 일대일 관계
(6) 다대다 단방향 [N:N]
- 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.
그래서 보통 다대다 관계를 일대다, 다대일 관계로 풀어내는 연결 테이블을 사용한다.
- 그런데 객체 2개로는 다대다 관계를 만들 수 있다. 회원 객체는 컬렉션을 사용해 상품을 참조하고, 반대로 상품들도
컬렉션을 사용해 회원을 참조하면 된다.
- @ManyToMany를 이용하면 다대다 관계를 편리하게 매핑 가능하지만 실무에 적합 X
(7) 다대다 양방향 [N:N]
- 회원과 상품. 회원은 여러 개의 상품을 구매할 수 있고 상품은 여러 회원에 의해 구매됨.
@ManyToMany의 한계
- 연결 테이블에 단순히 주문한 회원 아이디와 상품 아이디만 담고 끝나지 않는다. 보통은 주문 수량 컬럼이나 주문한
날짜 같은 컬럼이 더 필요하다.
- 컬럼을 추가해도 회원이나 상품 엔티티에 추가한 컬럼을 매핑할 수 없다
다대다 -> 일대다 + 다대일
- 결국, 연결 테이블을 매핑하는 연결 엔티티를 만들고 이곳에 추가한 컬럼들을 매핑해야 한다.
- 엔티티 간의 관계도 테이블 관계처럼 다대다 -> 일대다 + 다대일 관계로 풀어야 한다.
다대다 -> 일대다 + 다대일 (새로운 기본 키 사용)
- 복합 키를 만들지 않고 새로운 기본 키를 데이터베이스에서 자동으로 생성해주는 대리 키로 생성하는 방법
- 이 때 받아온 식별자는 외래 키로만 사용하고 새로운 식별자를 추가하는 관계를 비식별 관계라고 한다.