스프링 공부/JPA

[JPA] 17. 로딩(미안하다 이거보여주려고 어그로끌었다.)

장아장 2022. 12. 23. 22:01

미안하다 이거 보여주려고 앞에 프록시 설명하고 어그로 끌었다.

프록시를 배워서 그래서 뭘 어떻게 하는걸까???

프록시를 이용해 데이터를 한번에 우다다 DB에서 가져올 필요가 없어진다. 

 

예를 들어, 게임 로그인을 해서 캐릭터를 불러왔다고 해보자. 

길드, 친구멤버들의 정보 모두, 장비들, 스킬들, 펫, 아이템들 등등....

전부를 한번에 불러온다??? (내가봤을 때 로그인할 때 마다 게임 터진다...)

 

이를 대비해서 지연로딩이 존재한다. 

지연로딩을 통해, 연관된 객체들을 전부 불러오는게 아니라 그놈의 프.록.시 로 불러온다. 

@~~To~~의 외래키 조인을 할 때, (fetch = FetchType.~~)을 통해 즉시로딩과 지연로딩을 정할 수 있다. 

이런식으로 만들어진 상태에서 Member를 영속화 시킨 후, 영속성 컨텍스트를 clear시킨다고 해보자. 

이 후 Member를 조회할 땐, DB에서 멤버를 찾아와 보여주게 된다. 

 

이 떄!!! 멤버에 다대일로 매핑되어있는 팀은 프록시로 가져오게 된다. 

(미안하다. 이거보여주려고 앞에 프록시 정리해두면서 어그로 끌었다. )

 

이 후 팀에 대한 데이터를 조회할 때, 그 때야 Team을 DB에서 찾아온다. 

 

하.지.만? 

예를 들어 멤버를 조회할 때 팀을 항상 사용한다면?

FetchType.EAGER를 이용하는게 나을 수??? 있다.(즉시 로딩)

밑줄 친 이유는 아래에 적혀있다. 

이 때는 쿼리문이 join 쿼리로 같이 나오게 된다. 

 

이를 통해 팀과 멤버에서 해당 데이터를 한번에 찾아와 사용한다. 

즉, 멤버에 대한 데이터를 사용한 후 팀을 사용할 떄 쿼리문이 따로 나가지 않는다. 

 

!!!!!!주의사항!!!!!

실무에서는 즉시로딩을 쓰지 말자. 

왜?? 

즉시로딩은 JPQL에서 N+1 문제라는 것이 생긴다. 

연관관계에서 설정된 엔티티를 조회할 경우 조회된 데이터 갯수 만큼 연관관계의 조회 쿼리가 추가로 발생한다. 

 

이를 해결하기 위해선 FetchJoin을 사용해야 한다. 

이런식으로 쿼리문을 생성해 사용하는 방식이다. (나중에 이 부분도 공부해서 정리해봐야 할 것 같다)

 

 

영속성 전이 : CASCADE

DB에서 배운 cascade = 외래키를 이용한 연관관계에서는, cascade로 외래키가 주어져있다면 외래키의 데이터를 삭제하면 외래키로 가지고 있던 데이터도 같이 삭제하는 방식이었던 걸로 기억한다. 

 

JPA에서의 영속성 전이(CASCADE) = 연관관계로 얽힌 엔티티들이 존재할 떄, 그 중 특정 엔티티를 영속화시키면 연관된 엔티티 모두 영속화된다. 

사용법 : 외래키를 가지지 않는 엔티티의 연관관계 어노테이션( -> @OneToMany)에 mappedBy가 들어간다. 

그 옆에 넣어주면 된다 ex) @OneToMany(mappedBy=“parent”, cascade = CascadeType.PERSIST)

이를 통해 persist(뒤지게 많이 보았던) 영속화를 전이시킨 상태로 만들 수 있다. 

저장(영속화)과 삭제를 함께 움직이게 할 때에는 ALL

저장만 같이하고 삭제는 따로 움직일 때 PERSIST

저장은 따로 하지만 삭제를 같이 하게 할 땐 REMOVE를 뒤에 넣으면 된다. 

*** 영속성 전이를 쓰면 안될 때 : 해당 엔티티를 영속성 전이되어있는 엔티티외의 다른 곳에서도 쓴다면 영속성 전이를 시키는 것을 다시 생각해보자. 

예를 들어, 카테고리에 상품이 영속성 전이되어있다면? 카테고리가 변경되어 다른 곳으로 넘어가야 할 때 상품이 모두 같이 움직여버리고 이 때, 삭제가 된다면 상품의 판매자 측의 상품은 어떻게 하는가?

파는 사람이 지우지도 않은게 카테고리때문에 지워진다? 오바쥬?

즉, 단일 소유관계가 존재할 때 사용하는 것이 적당하다. 

 

중요한 것 : 영속성 전이와 연관관계 매핑은 따로 생각해야한다. 

연관관계가 매핑 되어있다고 모두 영속성을 전이시켜야 하는 것은 아니며,

영속성 전이가 연관관계를 의미하는 것은 아니다. 

 

 

고아 객체 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제

이를 어노테이션에 orphanRemoval = true로 설정해 사용할 수 있다. 

이렇게 했을 때의 장점 : 원래 연관관계에서 컬렉션에서 인스턴스의 Child를 삭제해도 해당 엔티티는 데이터베이스에서 삭제되지 않는다. 

하지만, orphanRemoval = true 상태일 때에는 컬렉션에서 Child를 삭제해도 Delete 쿼리문이 나가게 된다.  

 

주의점***

참조하는 곳이 하나일 때에만 사용한다! 다른 곳에서도 참조될 때 사용하면 다른 부분들에서 null값, 혹은 에러가 발생할 수 있다. 

@OneToOne, @OneToMany에만 사용할 수 있다. 

 

 

영속성 전이 + 고아객체, 생명 주기

CascadeType.ALL + orphanRemoval = true를 사용했을 때, 

영속성 전이를 통해 자식 엔티티의 생명주기 = 부모 엔티티의 생명주기로 맞출 수 있다. 

이는 도메인 주도 설계시에 Aggregate Root개념을 구현할 유용하다.