이모저모

JPA - Entity mapping, 연관관계의 주인 본문

coding/Java, Spring

JPA - Entity mapping, 연관관계의 주인

Jeo 2022. 2. 21. 16:33

자바 ORM 표준 JPA 프로그래밍 강의(김영한) 필기

🖍  객체와 테이블 매핑

  • @Entity가 붙은 클래스는 JPA가 관리.
  • 기본 생성자가 필수적으로 있어야 함.(public, 또는 protected 생성자)
  • final, enum, interface, inner 클래스 사용하면 안됨.

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

DDL(Data Definition Language): 데이터 전체 골격을 결정/정의하는 역할의 언어

spring으로 작업하면서, application.properties에 이런 식으로 작성했었는데, update외에도 create, create-drop, validate(엔터티와 테이블이 잘 매핑되어 있는지만 확인) 등의 옵션이 있다. 

spring.jpa.hibernate.ddl-auto=update

운영장비에는 절대로! create, create-drop, update 사용해서는 안된다고 함!

단계별 권장 설정(by 김영한님)

  • 개발초기에는 create, update 
  • 테스트 서버는 update, validate
  • 스테이징과 운영 서버는 validate 또는 none

DDL 생성기능

제약조건추가 @Column(nullable = false, length = 10, unique=true) 이런 식으로 db에 영향.

JPA의 실행로직에는 영향을 주지 않는다고 함.

🖍 필드와 컬럼 매핑

@Column(name = "name", nullable=false, ...)

컬럼 매핑

 

@Enumerated(EnumType.STRING)

enum 타입을 매핑

✔︎ 주의 EnumType.ORDINAL(이게 default) 사용하지 않기 _권장 by 김영한님

  • 왜 int형으로 저장되는 기본(ORDINAL설정)을 사용하면 안되는가? 
  • 비즈니스 로직에 의해 예컨대 회원의 유형이 추가될 수도 있다. 이때 새로 추가된 유형이 0번째에 추가되면, 이전에 0으로 입력된 친구랑 db상에서 구분을 할 수 없게 되는 실수(?) 가능하다. 차라리 메모리를 조금더 쓰더라도 확실하고 안전하게 STRING으로 enum이 들어가게 해놓는 것이 데이터 관리에 좋다.

@Transient

매핑 무시. 데이터베이스에는 반영 안하고 싶고 메모리에서만 임시로 계산해보거나 하고 싶을 때.


🖍 기본키 매핑

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

strategy 종류

  • IDENTITY
    • 기본 키 생성을 데이터베이스에 맡기는 것. 그런데 AUTO_INCREMENT의 경우 INSERT SQL 실행 후에야 ID 값을 알 수 있으므로,  IDENTITY 의 경우는 em.persist()시점에 INSERT SQL을 즉시 실행하여 식별자 조회 가능. 그러다보니 IDENTITY 전략의 경우 모아서 query들을 넘기는 것이 불가능하다는 단점(?!)이 있음.
  • SEQUENCE : 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트. 오라클, H2, PostgreSQL 에서 사용
  • AUTO: 방언에 따라 자동 지정, 기본값

🖍 연관관계 매핑

✔︎ 테이블의 FK는 애초부터 양방향이고, 객체의 참조는 단방향이므로 서로에 대한 참조들을 만드려면 단방향 두 개를 만들어야 한다.

  • 테이블 차원에서는 FK만 있으면 이쪽에서 저쪽, 저쪽에서 이쪽으로의 오고감이 가능하다. (사실상 테이블엔 방향이라는 것이 없음)
  • 하지만 객체 차원에서는 member 객체로부터 team에 대해 참조하고 있다고 해서, team객체에서 자연스럽게 member에 대한 참조/탐색이 가능한 것은 아니다. 

✔︎ 양방향 관계(즉 엄밀히 말해서 단방향 두개)를 만들 경우 주의점!

  • 예를 들어, Member (n) --------- (1) Team. 이렇게 두 엔티티가 있다고 해보자.
  • 나는 member a의 팀을 team A 가 아니라 B로 변경하고 싶다.
  • 그렇다면 나는 Member 엔터티에 접근해서 변경해야 할까, Team 엔터티에 접근해서 변경해야 할까?
  • (이 고민이 가능한 이유는, Member에서도 ManyToOne으로 참조하는 Team이 존재하고, Team에서도 OneToMany로 참조하는 List<Member>가 존재하기 때문이다.)
  • 이런 애매함을 해결하기 위해서는, 결정해야 한다. 무엇을? "연관관계의 주인"을!
  • 연관관계의 주인만 연관관계에 대해서 수정 등을 할 수 있고, 주인이 아닌 쪽에서는 '조회만 가능'!
  • 그렇다면 연관관계의 주인은 어떻게 정할 것인가?
  • 답은 "외래키를 가지고 있는 곳!!!"
  • Member와 Team의 관계에서 보자면, many쪽인 member가 팀에 대한 외래키를 갖는다고 볼 수 있다. 
  • (주의할 점은, "연관관계의 주인"이라는 어휘의 느낌 때문에 더 묵직한(?) Team이 주인이 아닐까 싶지만 그렇지 않다는 것!)
  • 그래서 team.getMembers().add(member)이런 식으로만 처리하면, team은 연관관계의 주인이 아니므로, 이 양방향 관계에 반영이 안된다..!!!!!
  • ==> 양방향 매핑시 연관관계의 주인 쪽에 값을 입력해야 한다.
  • 다만, 정말 객체지향답게 하자면 늘 양쪽 모두에 값을 입력해주어야 한다고 함!!
  • 물론 주인쪽에만 값을 입력해도, jpa가 양쪽 테이블에 다 sql query를 날려준다.
  • 하지만 혹시라도 sql에 들어가지 않은 상태에서 (즉 1차캐시에 머물러 있는 상태에서) 주인쪽(예-member)으로만 값을 변경하고, 반대편(예-team)에는 변경하지 않은 채로 이 둘을 불러내본다면, team은 쿼리 반영이 안 된 채로 1차캐시에 머물러있던 아이가 호출된다. 그래서 안정적으로 또 객체지향스럽게 처리하려면 양쪽 모두에 값을 입력해야 한다. 

'coding > Java, Spring' 카테고리의 다른 글

JPA의 데이터 타입  (0) 2022.02.22
JPA _ proxy, eager loading, lazyloading  (0) 2022.02.21
JPA - 영속성 컨텍스트  (0) 2022.02.21
절차/객체/관점 지향 프로그래밍  (0) 2022.02.21
request scope와 provider, proxy  (0) 2022.02.17
Comments