일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- JPA
- AOP
- 세션
- 백준
- 스프링부트
- Paging
- mock
- 프로그래머스
- 스프링 부트
- 우테코
- 우아한세미나
- yml
- HTTP
- 트랜잭션
- 자바
- 미션
- 우아한테크코스
- 프리코스
- CircuitBreaker
- Level2
- 의존성
- Spring Batch
- JUnit5
- 코드리뷰
- Docker
- 레벨2
- AWS
- MSA
- 서블릿
- REDIS
- Today
- Total
늘
[JPA] 양방향 매핑 OneToOne Lazy 이슈 본문
OneToOne에서 새로운 이슈를 맞이했다... 쿼리가 두번 나가는 즉, eager로 로딩이 된다.
바로 OneToOne으로 Lazy 로딩을 사용하려면 따로 설정을 해야한다. 또한 OneToOne관계는 fk를 어디에 두느냐에 따라 성격이 달라져서 잘 생각해보고 정해야한다.
결론부터 말하자면 3가지 처리가 필요하다.
- nullable이 허용되지 않는 @OneToOne 관계. (ex: Plan과 PlanResult)
- 양방향이 아닌, 단방향 @OneToOne 관계. (parent -> child)
- @PrimaryKeyJoin은 허용되지 않음.
optional 을 false 로 해두는 값은 CascadeType.PERSIST 와 같이 동작 안한다라고 한다.(null일 수 없다)
위의 사진은 주 테이블에 fk를 둔 케이스이다. << OneToOne관계에서 추천하는 방식(갠적인 의견!)
객체지향적 설계에 가깝고, 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인이 가능하다.
양방향
그렇다면 양방향 매핑으로는 Lazy loading이 안되는 걸까..?
http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html 여기서 친절히 설명해준다.
연관관계의 주인이 호출할 때는 지연 로딩이 정상적으로 동작하지만, 연관관계의 주인이 아닌 곳에서 호출한다면 지연 로딩이 아닌 즉시 로딩으로 동작한다는 것을 알 수 있다.
이렇게 지연 로딩으로 설정이 되어있는 엔티티를 조회할 때는 프록시로 감싸서 동작하게 되는데, 프록시는 null을 감쌀 수 없기 때문에 이와 같은 문제점이 발생하게 된다. 즉, 프록시의 한계로 인해 발생하는 문제이다.
예시
간단히 양방향 User와 Cart의 양방향 예시를 들겠다.
@Entity
public class User {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CART_ID")
private Cart cart;
}
@Entity
public class Cart {
@Id
@GeneratedValue
@Column("CART_ID")
private Long id;
@OneToOne(mappedBy = "cart", fetch = FetchType.LAZY)
private User user;
}
CART라는 테이블에는 USER를 참조할 수 있는 컬럼이 존재하지 않는다. 따라서 CART는 어떤 USER에 의해 참조되고 있는지 알 수 없다.
CART가 어떤 USER에 의해 참조되고 있는지 알 수 없다는 뜻은 만약 USER가 null이더라도 CART는 이 사실을 알지 못한다는 것이다.
만약 USER가 null이 아니라고 해도, CART의 입장에서는 USER가null인지 null이 아닌지 확인할 방법이 없다.
따라서 USER의 존재 여부를 확인하는 쿼리를 실행하기 때문에 지연 로딩으로 동작하지 않는 것이다.
그런데 양방향은 오류나기도 쉽고 불편하기때문에 다르게 설계하는 방법도 한 방법이라고 생각된다.
- OneToOne은 최대한 피하는게 좋다.
- OneToOne은 자식 먼저 저장해야하고(부모에서 자식 pk를 갖는 구조),
- OneToMany는 부모 먼저 저장해야한다(자식에서 부모 pk를 갖는 구조).
- OneToMany도 피하는게 좋다! (컬랙션이라서 나중에 query문제도 있고, 페이징처리할때도 번거롭다!)
- 구조를 유지한채 해결하기
- CART를 조회할때 USER도 함께 조회한다. (Fetch Join)
- batch fetch size를 사용한다.
Reference
'백앤드 개발일지 > 스프링부트' 카테고리의 다른 글
[Spring] JDBC 와 JDBC Template 이용해보기 (0) | 2021.08.15 |
---|---|
Servlet과 Controller(feat. Serializable) (0) | 2021.07.22 |
스프링 프레임워크 (0) | 2021.07.02 |
JPA, Hibernate, Spring Data JPA (0) | 2021.06.27 |
스프링 시큐리티 + _csrf설정 (0) | 2021.03.28 |