1탄
*너무너무너무 졸려서 글이 두서없음 주의.. 기록용으로 적음
JPA 를 사용하는 경우 영속성 컨텍스트를 사용하게 된다.
그리하여 영속성 컨텍스트를 사용하겠다는 표시인 @Transactional 애노테이션이 붙어있을 때야만 LAZY 로딩 객체가 제대로 가져와짐.

트랜잭션은 최대한 작은 단위로 쪼개져야하기 때문에 (안그러면 락이 길어지자나..)
나 같은 경우 이런식으로 두 메시지 저장을 한 트랜잭션에 넣었음

근데 메서드에 @Transactional 애노테이션이 붙어있지 않으면 ,,, 우리는 영속성 컨텍스트를 활용할 수 없음 ㅜㅜ
LAZY 로딩인 객체들을 가져올 수 없다 이거임..
참고) 조회용 메서드라면 read only 옵션을 추가하면 되긴 하는데 이건 조회 후 -> 생성하는 로직이라 read only 옵션은 적합하지 않음
그리하여 저는 어떻게 하기로 했냐면요

@Query("""
select cs from ChatSession cs
join fetch cs.diary d
join fetch d.user
where cs.id = :id
""")
Optional<ChatSession> findWithDiaryAndUser(Long id);
연관 된 데이터를 fetch join 하여 한번에 다 가져오도록 했답니다!
만약 @Transactional 을 메서드 레벨에 붙인다면
@Transactional
public CreateChatMsgRes createChatMessage(Long chatSessionId, CreateChatMsgReq req) {
User user = userService.getActiveUser(req.userId());
ChatSession chatSession = chatSessionRepository.findById(chatSessionId).orElseThrow(() -> {
return ChatException.chatSessionNotFound(chatSessionId);
});
if (chatSession.getDiary() == null) {
// throw exception
}
if (chatSession.getDiary().getUser() == null) {
//throw exception
}
if (!user.equals(chatSession.getDiary().getUser())) {
throw ChatException.chatSessionUserMismatch(
chatSession.getId(),
user.getId(),
chatSession.getDiary().getUser().getId()
);
}
...(생략)
}
- 트랜잭션이 넘 길어짐 ㅜㅜ
- 일일히 null 체크 코드를 추가해줘야 함 -> 코드가 더러워짐
- 조회 쿼리를 계속 날려야 함.. too bad ~
@Transactional 을 메서드 레벨에 붙이지 않고 findById 만 사용할 경우
- 일일히 조회 코드를 작성해줘야 함 -> 코드가 더러워짐
- 역시 조회 쿼리를 계속 날려야하니 bad
하지만 fetch join 을 할 경우
Hibernate:
select
cs1_0.chat_session_id,
cs1_0.bot_id,
cs1_0.completed_at,
cs1_0.created_at,
cs1_0.diary_id,
d1_0.diary_id,
d1_0.content,
d1_0.created_at,
d1_0.date,
d1_0.deleted_at,
d1_0.emotion,
d1_0.encryption_context_id,
d1_0.summary_short,
d1_0.updated_at,
d1_0.user_id,
u1_0.user_id,
u1_0.bot_id,
u1_0.created_at,
u1_0.deleted_at,
u1_0.email,
u1_0.encryption_context_id,
u1_0.nickname,
u1_0.password,
u1_0.is_sns,
u1_0.updated_at,
cs1_0.encryption_context_id,
cs1_0.send_count,
cs1_0.status,
cs1_0.summary
from
mmebot.chat_session cs1_0
join
mmebot.diary d1_0
on d1_0.diary_id=cs1_0.diary_id
join
mmebot.users u1_0
on u1_0.user_id=d1_0.user_id
where
cs1_0.chat_session_id=?
테이블을 한번에 조회하기 때문에 조회 쿼리는 단 한 번 !
이렇게 (fetch)join 을 이용할 경우 필요한 데이터를 한번에 처음부터 가져와, 위기를 모면할 수 있답니다

아 굿ㅋ
좋다 좋아
경사났네 경사났어
queryDSL 을 적용할까도 했지만
일단 간단하게 빨리빨리 개발하고 싶어서 JPQL 을 사용함..
좀 정리되고 나면 또는 더 더 복잡한 쿼리를 사용해야한다면 그 땐 QueryDSL 사용을 고려해보겠음...

사실 코드를 좀 더 수정해야해서...(더 완벽한 검증 코드를 추가하는 것이 필요)
일단 여기까지 쓰겠당 ㅜ
너무너무너무너무졸리고 피곤하여 글이 두서가 없을 텐데
사실 이 글 보고 JPA 에 대해 배우려는 분 없으시잖아요? 없죠..?(ㅎㅋㅎㅋ..)
그래서 그냥 나 혼자 여기까지 정리하고 마무리지음.
'개발 잡담' 카테고리의 다른 글
| 김영한의 실전 데이터베이스 - 설계 1편 후기 (0) | 2026.03.17 |
|---|---|
| 2년차 개발자가 되어버린 나. 회고록. (1) | 2026.02.06 |
| DB 논리적 제약 조건일 때 애플리케이션에 검증 코드를 추가해야 하는가? (0) | 2025.12.18 |
| Virtual Thread 가 뭣이당가? (1) | 2025.10.05 |
| 인생 처음으로 개발자로서 지냈던 지난 1년을 회고하며 (2) | 2024.12.19 |