본문 바로가기

Spring boot

[spring boot] 2차 정리 (연관관계 매핑 기초, 다양한 연관관계 매핑, 고급 매핑)

단방향 연관관계

persist하면 pk값 생성됨.  

객체를 DB에 맞춰 셋팅하면 문제 생김 : 

MEMEBER 첫번재 데이터_ID가 2인 이유: 공용 sequence를 사용해서 TEAM의 첫번째 데이터 _ID가 1이기 때문에...!

=> 따로따로 ID를 1부터 사용하려면 식별자 맵핑해주면 됨. 

------------

객체지향 : 객체를 참조해서,  객체  대상으로 쿼리 (JPQL)

    DB     : 외래키를 가지고, 테이블 대상으로 쿼리 (SQL)

MEMBER는 객체인 TEAM을 가지고 있지만 DB는 TEAM의 키를 가지고 있음.

MEMBER의 팀을 b팀으로 바꾸면 jpa는 객체가 바뀌는데 DB는 키값만 바뀜

----------

persist 하고 바로 find 하면 1차캐시에서 들고오기 때문에 select 쿼리 안 날라감 

DB에서 가져오고 싶으면 em.persist(); => em.flush(); DB와 싱크맞추기 => em.clear(); 영속성컨텍스트 초기화

 

 

양방향 연관관계와 연관관계의 주인 2 - 주의점, 정리

member와 team 중 (1:n일 때 n쪽이 주인) member가 연관관계의 주인. 

연관관계 설정할 때 주인에게는 값을 꼭 넣어줘야한다. 

jpa가 mapped by한건(주인 아닌얘) update, insert할 때 안 봄. 따라서  team의 members는 읽기 전용임

member.setTeam(team);  꼭 해야됨.

team. getMembers().add(member);  이건 해도 안해도 상관x but 얘도 하는게 좋음 

 

순수 자바 코드로 테스트 할 경우를 생각했을 때(진짜 객체 지향으로 생각하면)는 양쪽에 다 값을 넣어줘야 양방향 연관관계가 유지됨.

그리고 양쪽에 다 값을 넣어줘야 flush, clear 안했을때도 persist만 했을때 1차 캐시에서 가져와 뒤에서 변수 사용가능함. 

team 객체만 만들면 join이 아직 안 된 team 객체만 1차캐시에 있음. team의 멤버를 물으면 select 쿼리가 날라가지 않음. (1차 캐시에는 join이 안되어 멤버가 채워져있지 않기 때문)

---------------

연관관계 편의 메소드를 생성하자. 

연관관계 편의메소드란?

team.getMermbers().add(member)를 메인에서 따로 해주지 말고 

Member entity에 가서 setTeam 할때 team.getMermbers().add(this)를 해주기 

그리고 setTeam 이름은 changeTeam으로 이름 바꿔주기

*자바 메인에는 member.changeTeam(team)코드가 된다.

어쨌든 양쪽에 값 다 셋팅해줘야함 -> 연관관계 편의 메소드는 1이나 n이나 어디에나 해도 됨.  

------------

!조심! !조심! !조심! 

Member의 toString은 Team의 toString을 부르고 Team의 toString은 Member의 toString을 부르며 서로 계속 호출함. 무한루프에 걸리지 않게 둘 중 하나는 빼고 써라

controller에서는 entity를 dto로 변환해서 반환해라

설계는 단방향맵핑으로 해두고 개발할때 반대방향으로 조회기능을 추가시키기(개발할때 정말 필요하면 양방향으로 만들기)

 

 

실전 예제 2 - 연관관계 매핑 시작

객체 지향적 x : member의 id 찾는 쿼리 날리고 find (Team.class,member.id)쿼리 한번더 날림 

객체 지향적 o: member.getTeam() 쿼리 한방에 team 찾음 

-----------

핵심! 단반향을 먼저 하고 비지니스상 양방향이 더 좋으면 개발하다 바꾸자

 

 

일대다 [1:N]     (거의 사용 x)

1:다는 옆테이블에 update해야하기 때문에 update 쿼리가 한번 더 날아감.

@joinColumn 꼭 사용해야함(안 그러면 중간테이블 만들어짐) 

일대다인데 읽기 전용으로 양방향을 쓰고 싶을때(다대일과 똑같이 하되, 조인컬럼 안에 어쩌고 부분이 추가)

@ManyToOne

@JoinColumn(name = "", 어쩌고~~~~)써라 

이렇게 하면 값은 다 들어가지는데 최종적으로 읽기전용으로 DB에 insert, update가 안됨

--------------

핵심! 걍 다대일 써라~~~~~~~~

 

 

일대일 [1:1]

Member 객체에 locker필드가 있으며 Locker에는 member필드가 없다. 

따라서 MEMBER가 주 테이블, LOCKER가 대상 테이블

 

주테이블에 외래 키 단방향  

MEMBER에 LOCKER_ID가 있으면 Member객체에 @joinColumn 사용

주 테이블 아닌 곳에 @OneToOne(mappedBy="")

 

대상 테이블에 외래 키가 있는 단방향

아예 지원이 안됨~~~~ 이런건 없어요~~

 

대상 테이블에 외래 키가 있는 양방향

걍 Locker를 주인으로 두고 LOCKER의 MEMBER_ID와 연결시킴.  걍 주인을 Locker로 옮긴 주 테이블에 외래 키 양방향임....

 

 

DB 어디에 외래키를 둬야하나..?

비지니스 측면에서 나중에 MEMBER가 LOCKER를 여러개 사용할 수 있을 수 있다 생각하면 LOCKER에 MEMBER_ID가 있는게 나음 (UNIQUE 제약조건만 없애면 되기 때문)

BUT 반대는 코드 수정이 많이 필요함. 

따라서 비지니스 측면을 생각해 미래를 대충 생각하고 선택

개발자입장에서는 멤버에 locker_id가 있는게 좋음. MEMBER를 조회할 때가 더 많음.  LOCKER가 있는 사람과 없는 사람에 따라 뭐 나눠야하면 조인없이 쿼리 하나로 LOCKER여부(NULL인지) 알 수 있음! 

만약 LOCKER에 MEMBER_ID가 있는데 주로 Member를 통해 locker에 access하면 양방향으로 만들어야함.  

 

 

다대다 [N:M]

핵심! 다대다는 중간테이블을 엔티티로 승격해라!(다대다 쓰지말고 1:다, 다:1로 나눠라)

 

!주의! 웬만하면 pk는 데이터와 상관없이 generateValue로 하길..!

 

 

 

실전 예제 3 - 다양한 연관관계 매핑

다대다는 @JoinTable(name="",joinColumns = @JoinColumn(name=""),inverseJoinColumns = @JoinColumn(name ="")) 내가 조인하는건 이거고 반대편애가 조인하는건 얘야. 중간테이블이 있다 치고 만들기 

 

 

 

상속관계 매핑

기본적으로 단일테이블전략으로 생성되는데 Item에 @Inheritance(전략 어쩌고 join)을 해주면 조인전략으로 테이블 생성됨.

@DiscriminatorValue을 부모에 쓰면 DTYPE이 저절로 생김 (DTYPE은 자식 엔티티의 이름이 들어감)

@DiscriminatorValue("")을 자식에 쓰면 부모의 DTYPE필드에 ("")명이 들어감 

@DiscriminatorValue

Item 

-----

@DiscriminatorValue("M")

Movie면 

 

---------DB에는

       ITEM               MOVIE

DTYPE  |  Id                Id

    M   |   1                 1               id는 둘이 같음 

 

----------------------------

싱글테이블은 @DiscriminatorValue안 넣어도 DTYPE 생김

 

 

전략 table_per_class 

ITEM을 없애고 ALBUM, MOVIE 등에 걍 ITEM 속성들을 각각 넣어 중복돼도 상관없이 . 

전략 table_per_class하고 Item을 abstract로 만들면  ITEM안 만들어지고 MOVIE에 name, price등이 들어감. 

이때는 @DiscriminatorColumn은 DTYPE 안 쓰니까 필요없어서 써줘도 효과 X

BUT movie의 id만 알면 모든 테이블(album, book)도 다 뒤져야해서 쿼리가 복잡 

나머지 전략은 싱글테이블 전략일경우 id알면 한 테이블만 뒤지면 되고 조인전략인 경우 id알고 Dtype알면 바로 알 수 있다. 

!주의! 구현클래스마다 테이블 전략은 쓰지 마라~~~~

 

핵심!

비지니스적으로 중요하고 복잡함, 데이터가 많으면 조인

데이터도 얼마없고 단순하면 싱글테이블 

보통은 조인 많이 씀

 

 

 

 

Mapped Superclass - 매핑 정보 상속

DB는 완전히 개별적인데 객체에는 같은 속성을 부모로 만들어서 속성만 받아서 쓰고싶을 때! (등록일, 수정일에 유용)

핵심! 상속은 절대 아님 

 

부모는 @MappedSuperclass해주고 @Entity 하지말기. 자식은 extends 하고 상속받기 

 

------------------ 구현클래스마다 테이블전략와 차이

구현클래스마다 테이블전략은 movie의 id로 검색하려면 album, book도 다 union해서 검색해야되는데 얘는 걍 ㄹㅇ 지 혼자 독립적인 애라 바로 검색가능 

-----------------

super에 @Entity가 있으면 상속관계맵핑(윗 내용)이고 

@MappedSuperclass가 있으면 속성만 상속할때 씀

 

 

 

 

실전 예제 4 - 상속관계 매핑

item만 따로 만들 일이 있냐 없냐로 abstract의 여부 결정

album, book, movie는 같은 Item묶음이니까 entity를 써주고 날짜를 쓸 때 delivery, order, item은 같은 묶음이 아니니까 @MappedSuperClass로 속성만 내려 사용