일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 힙
- 데코레이터
- 일대다
- execute
- JPQL
- 다대다
- 지연로딩
- querydsl
- SQL프로그래밍
- 즉시로딩
- 다대일
- 동적sql
- shared lock
- 스프링 폼
- 비관적락
- exclusive lock
- PS
- 백트래킹
- eager
- 스토어드 프로시저
- 유니크제약조건
- 연결리스트
- fetch
- 낙관적락
- 이진탐색
- CHECK OPTION
- 연관관계
- FetchType
- dfs
- BOJ
- Today
- Total
흰 스타렉스에서 내가 내리지
[JPA] findById() 와 getReferenceById() 본문
# findById 를 사용했을 경우
Term termPS = termRepository.findById(requestDto.getTermId())
.orElseThrow();
Member memberPS = memberRepository.findById(memberId)
.orElseThrow();
// 북마크 테이블을 업데이트 합니다.
Optional<TermBookmark> termBookmarkOptional = termBookmarkRepository.findByTermAndMember(termPS, memberPS);
if (termBookmarkOptional.isEmpty()){
termBookmarkRepository.save(TermBookmark.of(termPS, memberPS));
}else{
termBookmarkOptional.get().addFolderCnt();
}
Hibernate:
select
t1_0.term_id,
t1_0.categories,
t1_0.description,
t1_0.name
from
term t1_0
where
t1_0.term_id=?
2024-02-08T18:48:51.095+09:00 TRACE 69831 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:BIGINT) <- [2]
Hibernate:
select
m1_0.member_id,
m1_0.categories,
m1_0.created_date,
m1_0.domain,
m1_0.email,
m1_0.folder_limit,
m1_0.identifier,
m1_0.introduction,
m1_0.job,
m1_0.modified_date,
m1_0.name,
m1_0.nickname,
m1_0.point,
m1_0.profile_img,
m1_0.refresh_token,
m1_0.role,
m1_0.social_id,
m1_0.social_type,
m1_0.year_career
from
member m1_0
where
m1_0.member_id=?
2024-02-08T18:48:51.096+09:00 TRACE 69831 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:BIGINT) <- [1]
Hibernate:
select
tb1_0.term_bookmark_id,
tb1_0.folder_cnt,
tb1_0.member_id,
tb1_0.status,
tb1_0.term_id
from
term_bookmark tb1_0
where
tb1_0.term_id=?
and tb1_0.member_id=?
2024-02-08T18:48:51.126+09:00 TRACE 69831 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:BIGINT) <- [2]
2024-02-08T18:48:51.126+09:00 TRACE 69831 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:BIGINT) <- [1]
Hibernate:
insert
into
term_bookmark
(folder_cnt, member_id, status, term_id, term_bookmark_id)
values
(?, ?, ?, ?, default)
쿼리가 총 4회 발생한다.
1. Term 엔티티를 SELECT 한다. -- findById()
2. Member 엔티티를 SELECT 한다. -- findById()
3. TermBookmark 엔티티를 SELECT 한다.
4. TermBookmark 엔티티를 INSERT 한다.
이 때, 4번 쿼리를 날리기 위해서, 1번, 2번 쿼리를 날려 Member 엔티티와 Term 엔티티를 불러오고 있다.
그런데, 코드 상에서 이미 memberId와 termId 를 알고 있고, 3번 쿼리를 날릴 때는 memberId와 termId 만 필요한데, 굳이 1번과 2번 쿼리를 날릴 필요가 있을까?
JPA에서 직접 연관 관계의 엔티티를 조회하지 않고 엔티티를 저장하는 방식은??
Term termPS = termRepository.getReferenceById(requestDto.getTermId());
Member memberPS = memberRepository.getReferenceById(memberId);
// 북마크 테이블을 업데이트 합니다.
Optional<TermBookmark> termBookmarkOptional = termBookmarkRepository.findByTermAndMember(termPS, memberPS);
if (termBookmarkOptional.isEmpty()){
termBookmarkRepository.save(TermBookmark.of(termPS, memberPS));
}else{
termBookmarkOptional.get().addFolderCnt();
}
Hibernate:
select
tb1_0.term_bookmark_id,
tb1_0.folder_cnt,
tb1_0.member_id,
tb1_0.status,
tb1_0.term_id
from
term_bookmark tb1_0
where
tb1_0.term_id=?
and tb1_0.member_id=?
2024-02-08T18:58:37.517+09:00 TRACE 69940 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter (1:BIGINT) <- [2]
2024-02-08T18:58:37.517+09:00 TRACE 69940 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter (2:BIGINT) <- [1]
Hibernate:
insert
into
term_bookmark
(folder_cnt, member_id, status, term_id, term_bookmark_id)
values
(?, ?, ?, ?, default)
쿼리가 단 2회만 발생하고 있다.
즉, 위에서 1번, 2번 쿼리가 발생하지 않았다.
XxxRepository.getReferenceById(id);
findById() 는 eager 방식의 조회기법이라면, getReferenceById() 는 lazy 방식으로 조회된다.
getReferenceById() 는 주어진 식별자를 가지고 있는 엔티티의 참조를 반환한다.
만약, 식별자가 DB 에 존재하지 않는다면, EntityNotFoundException 이 발생한다.
try-catch 문을 이용하여 적절히 예외처리를 해주어야 한다.
어 근데 왜 예외가 안 잡히지..?
Term termPS;
try {
termPS = termRepository.getReferenceById(requestDto.getTermId());
}catch (EntityNotFoundException e){
throw new CustomApiException("용어가 존재하지 않습니다.");
}
이렇게 했는데 EntityNotFoundException을 못잡는다.....?
Referential integrity constraint violation: "FK55L2J14KR3176XLXRT5Y1QHRI: PUBLIC.TERM_BOOKMARK FOREIGN KEY(TERM_ID) REFERENCES PUBLIC.TERM(TERM_ID) (CAST(1223 AS BIGINT))"; SQL statement: insert into term_bookmark (folder_cnt,member_id,status,term_id,term_bookmark_id) values (?,?,?,?,default)
당연하지요.
Lazy 방식으로 조회하기 때문에, Exception 이 저 코드에서 발생하지 않는다.
저 코드를 지나갈때는 있건 없건 SQL 이 발생하지 않는다.
그렇다면 언제 예외가 발생하느냐? 바로 termPS 를 비로소 사용할 때!
// 실제로 termPS 를 사용하여 예외가 발생할 부분!!!
termBookmarkRepository.save(TermBookmark.of(termPS, memberPS));
그렇다면 위 코드의 맨 아랫줄에 try-catch 문을 감싸주면 에러를 잘 처리해주지 않겠는가?
try {
termBookmarkRepository.save(TermBookmark.of(termPS, memberPS));
}catch (DataIntegrityViolationException e){
throw new CustomApiException("존재하지 않습니다.");
}
Body = {"status":-1,"message":"존재하지 않습니다.","data":null}
이제 예외처리를 잘 잡아준다.
위 코드에서는 DataIntegrityVioldationException 이 발생한다.
termPS 의 요소를 조회하여 본격적인 SELECT 문이 날아갈 때는 EntityNotFoundException 이 발생할 테지만, 지금은 FK 관련 에러이므로, DataIntegrityVioldationException 을 잡아줘야 한다.
FK의 제약 조건을 다음과 같이 풀어주면, 식별자가 존재하지 않아도 DB 에 저장된다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private Term term;
'Spring' 카테고리의 다른 글
JPQL 로 ORDER BY RAND() LIMIT N 사용하기 (0) | 2024.03.12 |
---|---|
쿼리를 5,000번 날리는 API가 있다?😬 쿼리 줄이기 8시간 삽질 후기 (0) | 2024.02.15 |
Spring Security + JWT 흐름 간략하게 (0) | 2024.01.21 |
서블릿 예외 처리 - 필터, 인터셉터 (0) | 2023.12.29 |
서블릿 (0) | 2023.12.28 |