개발을 하다가 엔티티 내부에 연관관계로 매핑된 다른 엔티티를 불러올 때 column 값은 있지만 해당 데이터가 DB에서 없는 데이터일 경우
쿼리 단에서 예외가 발생한다. 이를 어떻게 해결해야 할까?
//Board 엔티티 내부 필드
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
private User user;
Board 라는 엔티티 내부의 user 필드이다.
만약 user_id 가 null
인 값이 들어오면 어떻게 되나?
(board 테이블에 한 튜플의 user_id 컬럼이 null 인 경우)
-> 이런 경우 FetchType.LAZY, EAGER에 관계없이 찾으려는 시도조차 안한다.
즉 user_id에 null 값이 들어있으면 그냥 Board만 딱 들고 온다는 것
그러면 user에는 null이 들어가있는다.
그리고 이는 optional 이라는 옵션이 true로 기본적으로 설정되어 있기 때문 (디폴트가 true라 안보임)
//Board 엔티티 내부 필드
@ManyToOne(fetch = FetchType.EAGER, optional = true)
@JoinColumn(name = "user_id")
private User user;
핵심은 시도조차 안한다는 것 -> 쿼리가 안날아가니 문제될 게 없음.
당연히 NPE가 발생할 것이다.
그럼 필드를 Optional<User>
로 만들어 주면 되겠네? -> X
Optional은 엔티티 필드에 쓰면 안된다고 한다. (JPA 스펙상 그렇다고 함, JPA에서 사용 금지함)
그럼 어떻게 하지...
서비스단에서 Optional로 가야 한다.
Optional<User> optionalUser = Optional.ofNullable(board.getUser());
이렇게 통하면 null-safe 하게 사용할 수 있다.
-> 하지만 나는 null 체크를 잘 해야한다는 설명을 하고있는게 아니다.
설명하고 싶은 상황은 이러한 상황이다.
- Board 내부에는 User가 매핑되어 있다.
- user_id pk 값으로 DB에서 찾게된다.
- Board 테이블 내에는 user_id로 '10' 이라는 값이 존재한다.
- User 테이블에서 pk 10을 가진 user는 없는 경우
일반적으로 구현할 때 유저가 회원 탈퇴를 하면 soft_delete를 한다.
"is_delete" 라는 컬럼을 User에 두고 삭제가 된다면 true 로 바꿔주는 그런식의 삭제 로직을 사용하는데
이러한 세부적인 정책은 결국 고객의 요구사항에 맞게 반영해야 하는 부분이기 때문에 달라질 수 있다.
그래서 해당 프로젝트에서는 회원 탈퇴 시 hard-delete 즉 데이터를 삭제하는 방향으로 구현하게 되었다.
Board 엔티티를 가져오면 fetchType에 따라 User를 바로 가져올 수도 있고, 아니면 필요할때 가져올 수 있다.
-> SQL 에러가 발생한다.
이를 어떻게 해결했나?
이는 JPA의 기능은 아니고 hibernate에서 지원하는 기능이다.
//Board 엔티티 내부 필드
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
@notfound(action = notfoundaction.ignore)
private User user;
만약 user_id로 DB에서 검색을 했더니 데이터가 없는 데이터다? 그러면 예외를 발생시키지 말고 null을 넣어라
라는 어노테이션이다.
마치 Java Optional을 트랜잭션에 적용한 느낌이다.
나는 이런 어노테이션을 사용해서 예외 처리를 할 수 있었다.