참고 영상 : 인프런, 김영한 자바 ORM 표준 JPA 프로그래밍
기본 키 제약 조건
테이블은 연관된 테이블 모두 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가 있다면 어떻게 하지?
양방향 관계에서 나오는 개념이며 객체의 관계에서 하나를 주인으로 지정
그리고 주인만이 외래키를 관리합니다.
주인이 아닌쪽은 mappedBy 속성을 사용하며 read only 특징을 가지게 됩니다.
외래 키가 있는 곳을 주인으로 지정하기
요약
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 국한되는 양방향 참조가 아니라도 순환 참조는 컴퓨터 공학쪽에서는 피해야한다는 것이 불문율로 자리 잡혀있다고 알고 있습니다.
다중성
일대다 단방향 (권장 X)
일대일 단방향 (주 테이블에 외래 키)
일대일 양방향을 원한다면 반대편에 mappedBy 사용
주 테이블, 대상 테이블 중에 외래 키 선택 가능
일대일을 사용한다면 일대다로 후에 변경이 될 수 있다는 것을 감안하고
외래 키의 위치를 정하자
비즈니스상 조회가 더 많이 되는 쪽에 외래 키를 두어도 좋음
일대일 양방향 (대상 테이블에 외래키)
1. Member기준에서 Fetch Type Lazy 지원 X
Member를 조회한다면 Locker의 유무를 찾아야합니다.
Locker가 존재한다면 Proxy 객체, 없다면 Null이기때문에
어차피 처음부터 조회를 하기때문에 Lazy가 의미가 없음.
일대일 단방향 (대상 테이블에 외래 키)
지원 X
다대다 (사용하지 말자)
매핑의 3가지 방식
1. 조인 전략
Java에서 상위 class를 상속받는 하위 클래스와 Field 위치가 DB Table에 Join으로 하여금 동일한 포지션으로 구성
상속을 받는 역할을 하는 테이블은 PK, FK를 같은 컬럼으로 사용하며
상속을 하는 역할을 하는 테이블은 해당 Records가 어떤 형태를 가지는지
표현하는 컬럼을 가집니다.
정규화의 장점, 단점을 모두 가져가는 특징이 있습니다.
해당 전략을 사용 추천
2. 단일 테이블 전략
다형적인 형태들이 가지는 상태들을 1개의 테이블에 표현
위와 동일하게 Records가 어떤 형태인지 표현하는 컬럼을 가집니다.
Entity들 끼리 extends를 사용할 시 적용되는 기본 전략
Join이 없으므로 일반적으로 성능이 가장 좋음
하지만 모든 하위 클래스의 상태들이 모두 Nullable합니다.
정말 단순하고 확장 가능성이 없으며 비즈니스적으로 중요하지 않다면 추천
3. 클래스마다 테이블 구현 (추천 X)
이미지와 같이 형태들 마다 1개의 테이블이 생깁니다.
조회시 생겨난 테이블을 다 찾아봐야하는 단점이 생깁니다.
특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을때 사용합니다.
연관 관계에서 소유자가 1개이며 생명 주기가 거의 유사하면 사용합니다.
하지만 여러 Entity와 얽혀있거나 하면 운영이 어려워집니다.
자주 사용되는 속성
ALL : 모두 적용
PERSIST : 영속
REMOVE : 삭제
부모 엔티티와 연결이 끊어진 자식 엔티티
OrphanRemoval 속성을 true로 하면 연관 관계 제거시에
자동으로 고아 객체가 삭제됩니다.
OrphanRemoval 속성은 CASCADE처럼 연관 관계에서 소유자가 1개 + 생명주기가 유사시에만 사용을 권장합니다.
CASCADE ALL과 OrphanRemoval ture로 관리하면
자식의 생명주기를 관리할 수 있습니다.
도메인 주도 설계에서는 Root Entity에서 Sub Entity로 적용도 가능합니다.
(Sub Entity의 Repository를 패스 가능!)