JPA - 기본 개념 3편

minseok·2023년 3월 4일
0

참고 영상 : 인프런, 김영한 자바 ORM 표준 JPA 프로그래밍






권장하는 식별자 전략

기본 키 제약 조건

  1. not null
  2. unique
  3. immutable

권장 사항
  1. 비즈니스가 담긴 값만 사용 X

    주민등록번호만을 Primary key로 사용시 정책에 의하여
    회사에서 보관을 금지하면 문제가 생긴다.
    Long + 대체키 + 키 생성전략 사용하기







테이블과 객체 1 - 연관 데이터를 찾는 방법



  1. 테이블은 외래 키로 조인하여 연관된 테이블을 찾는다.
  2. 객체는 참조를 사용해서 연관된 객체를 찾는다.
  3. 객체와 테이블에는 큰 간극이 존재


테이블과 객체 2 - 양방향 연관관계



테이블은 연관된 테이블 모두 key를 가지고 있습니다.
테이블은 방향이라는 개념이 없습니다.
하지만 객체에서 단방향일때 N에서 1로 갈수있으나 1에서 N으로 갈수없습니다.
1에 N의 참조객체를 포함시켜야 1에서 N으로도 갈수있습니다.


객체와 테이블이 관계를 맺는 차이

객체

Member -> Team, Team <- Member
양쪽에 참조 객체를 넣어줘야해서 사실 단방향이 2개가 있는 것

Member.getTeam()

Team.getMembers()

테이블

MEMBER <-> TEAM, 1개의 외래키 1개로 서로를 알 수 있음.

SELECT *
FROM MEMEBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAMID

객체 Membe와 객체 Team중 어디서 MEMBER Table의 FK를 바꿔줄까?

Q. Member와 Team이 같이 영속 객체가 되었는데
Member는 Team이 없고 Team은 Member가 있다면 어떻게 하지?

  1. DB 세상에서는 MEMBER Table FK 값만 결과적으로 잘 변경해주면 된다.
  2. 객체 세상에서는 둘중 한놈만 업데이트 시켜주자. (연관관계의 주인)







테이블과 객체 3 - 연관관계의 주인





양방향 관계에서 나오는 개념이며 객체의 관계에서 하나를 주인으로 지정
그리고 주인만이 외래키를 관리합니다.
주인이 아닌쪽은 mappedBy 속성을 사용하며 read only 특징을 가지게 됩니다.

외래 키가 있는 곳을 주인으로 지정하기

  1. 1:N기준으로 Team의 상태를 수정시, 주인이 Team이라면
    Team에 포함된 모든 Member에 update query가 실행






양방향 mapping때 많이 하는 실수




요약

1. 문제 : 주인에 값 입력하지 않기
2. 문제 : 주인에만 값 입력하기
3. 두가지 방법을 한번에 해결하는 방법



1. 주인에 값 입력하지 않기

주인 객체 Member에서 관계 설정을 해줘야 정상적으로 반영됩니다.




2. 주인에만 값 입력하기

주인에만 값을 입력하는 경우

위의 코드에서 flush(), clear()를 수행하지 않으면
find()로 조회한 Team은 persist()를 수행했을때의 상태입니다.
주인에만 값을 입력하였기 때문에 members를 포함하지 않은 team이
1차 캐시에 존재합니다.

트랜잭션이 끝나기 전에 다시 조회를 할때는 flush(), clear()를 하여서
쓰기 지연 저장소의 쿼리를 적용시키고 1차 캐시의 데이터 삭제가 필요합니다.





3. 두가지 방법을 한번에 해결하는 방법

아래와 같은 method를 Team과 Member객체에 작성합니다.


이제 주인과 자식 양방향 모두 관계 설정을 하였으므로
정상적으로 DB에도 적용되며 트랜잭션 종료이전 Team을 조회해도
예상한 것 같은 관계가 설정이 되어있습니다.



4. 무한 루프문제
toString(), Lombok 라이브러리, Json 라이브러리 같은 것을 사용하는 경우
OSIV 영역 안에 있다면 서로를 계속 호출이 됩니다.(조건적으로)
Object 직렬화 기능을 제공하는 경우 보편적으로 가지고 있는 모든
상태에 대하여 시도하기 때문에 ignore 할 수 있는 방법 또한 알아야 합니다.

사실 JPA 국한되는 양방향 참조가 아니라도 순환 참조는 컴퓨터 공학쪽에서는 피해야한다는 것이 불문율로 자리 잡혀있다고 알고 있습니다.







정리

  1. 우선적으로 무조건 단방향 매핑만으로 완료하기
  2. 양방향은 정말 필요하다면 이후에 추가하기
  3. 비즈니스 로직을 기준으로 연관관계 주인 선택하지 말기
    무조건 키의 위치를 기준으로 설정하기
  4. 역방향은 JPQL 사용

다양한 연관관계 매핑






다중성

  1. 다대일 @ManyToOne (많이 사용)
  2. 일대다 : @OneToMany (권장 X)
  3. 일대일 : @OneToOne (단방향은 서버 개발자가 그나마 선호)
  4. 다대다 : @ManyToMany (사용 금지)







일대다 단방향 (권장 X)

  1. Database에 새로운 Team과 Member를 넣는다면
    Team, Member가 1개씩 Insert가 들어가고 후에
    Member Table의 FK update query가 들어감
  1. 복잡한 use case에 포함되어있다면 복잡성 유발
  1. "1"이 연관관계의 주인
  1. @JoinColumn을 사용하지 않으면 Join Table 사용
  1. 일대다 단방향보다는 그냥 다대일 양방향을 사용하자






일대일 단방향 (주 테이블에 외래 키)

일대일 양방향을 원한다면 반대편에 mappedBy 사용

  1. 주 테이블, 대상 테이블 중에 외래 키 선택 가능

  2. 일대일을 사용한다면 일대다로 후에 변경이 될 수 있다는 것을 감안하고
    외래 키의 위치를 정하자

  3. 비즈니스상 조회가 더 많이 되는 쪽에 외래 키를 두어도 좋음






일대일 양방향 (대상 테이블에 외래키)

1. Member기준에서 Fetch Type Lazy 지원 X

	Member를 조회한다면 Locker의 유무를 찾아야합니다.
    Locker가 존재한다면 Proxy 객체, 없다면 Null이기때문에
    어차피 처음부터 조회를 하기때문에 Lazy가 의미가 없음.






일대일 단방향 (대상 테이블에 외래 키)

지원 X






다대다 (사용하지 말자)

  1. Join Table 필요 정규화된 2개의 테이블로는 다대다 표현할 수 없음
  2. @OneToMany, @ManyToOne + JoinTable을 Entity로 승격해서 사용하기







상속관계 매핑

  1. 관계형 DB는 상속 관계가 없음
  2. 슈퍼타입 서브타입 관계라는 모델링 기법이 상속과 유사
  3. 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계 매핑





매핑의 3가지 방식

1. 조인 전략

Java에서 상위 class를 상속받는 하위 클래스와 Field 위치가 DB Table에 Join으로 하여금 동일한 포지션으로 구성

상속을 받는 역할을 하는 테이블은 PK, FK를 같은 컬럼으로 사용하며
상속을 하는 역할을 하는 테이블은 해당 Records가 어떤 형태를 가지는지
표현하는 컬럼을 가집니다.

정규화의 장점, 단점을 모두 가져가는 특징이 있습니다.
해당 전략을 사용 추천






2. 단일 테이블 전략

다형적인 형태들이 가지는 상태들을 1개의 테이블에 표현
위와 동일하게 Records가 어떤 형태인지 표현하는 컬럼을 가집니다.

Entity들 끼리 extends를 사용할 시 적용되는 기본 전략
Join이 없으므로 일반적으로 성능이 가장 좋음

하지만 모든 하위 클래스의 상태들이 모두 Nullable합니다.

정말 단순하고 확장 가능성이 없으며 비즈니스적으로 중요하지 않다면 추천




3. 클래스마다 테이블 구현 (추천 X)

이미지와 같이 형태들 마다 1개의 테이블이 생깁니다.
조회시 생겨난 테이블을 다 찾아봐야하는 단점이 생깁니다.








@MappedSuperClass


공통 매핑 정보가 필요할때 사용하며 자식 클래스에 공통 매핑 정보가 필요할 때 사용합니다.
직접 생성할 일이 없으므로 추상 클래스를 권장

@MappedSuperClass를 사용한 class를 상속받으면 사용 가능






CASCADE

특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을때 사용합니다.

연관 관계에서 소유자가 1개이며 생명 주기가 거의 유사하면 사용합니다.
하지만 여러 Entity와 얽혀있거나 하면 운영이 어려워집니다.

자주 사용되는 속성

ALL : 모두 적용
PERSIST : 영속
REMOVE : 삭제







고아 객체

부모 엔티티와 연결이 끊어진 자식 엔티티
OrphanRemoval 속성을 true로 하면 연관 관계 제거시에
자동으로 고아 객체가 삭제됩니다.

OrphanRemoval 속성은 CASCADE처럼 연관 관계에서 소유자가 1개 + 생명주기가 유사시에만 사용을 권장합니다.

CASCADE ALL과 OrphanRemoval ture로 관리하면
자식의 생명주기를 관리할 수 있습니다.
도메인 주도 설계에서는 Root Entity에서 Sub Entity로 적용도 가능합니다.
(Sub Entity의 Repository를 패스 가능!)

profile
즐겁게 개발하기

0개의 댓글