조회가 두번 일어나면 캐싱이 된다
기본키 조회는 find() 메서드가 최적
기본키로 단건 조회할 때는 EntityManger의 find() 메서드를 사용하는 것이 가장 효율적입니다.
find() vs JPQL 비교
- find(): 1차 캐시 활용, 기본키 최적화, 간단한 문법
-JPQL : 복잡한 조건이나 조인이 필요할 때 사용
====================================================================
package com.tenco.blog.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@RequiredArgsConstructor // 필수 멤버변수를 확인해서 생성자에 등록해줌
@Repository // Ioc 대상이 된다 - 싱글톤 패턴으로 관리 됨
public class BoardPersistRepository {
// JPA 핵심 인터페이스
// @Autowired final 를 사용시 사용 불가
private final EntityManager em;
// 게시글 한건 조회 쿼리 만들기
// em.find(), JPQL, 네이티브 쿼리
public Board findById(Long id) {
// 1차 캐시 활용
return em.find(Board.class, id);
}
// JQPL를 사용환 조회 방법 (비교용 - 실제로는 find() 권장)
public Board findByIdWithJPQL(Long id) {
// 네임드 파라미터 권장 사용
String jpql = " SELECT b FROM Board b WHERE b.id = :id ";
// Query query = em.createQuery(jpql, Board.class);
// query.setParameter("id", id);
// Board board = (Board) query.getSingleResult();
try {
return em.createQuery(jpql, Board.class)
.setParameter("id", id)
.getSingleResult(); // 주의점 : 결과 없으면 NoResultException 발생
} catch (Exception e) {
return null;
}
// JPQL 단점 :
// 1. 1차 캐시 우회하여 항상 DB 접근
// 2. 코드가 복잡하게 나올 수 있음
// 3. getSingleResult() 호출 <-- 예외 처리 해주어야 함
}
// JPQL을 사용한 게시글 목록 조회
public List<Board> findAll() {
// JPQL : 엔티티 객체를 대상으로 하는 객체지향 쿼리
// Board는 엔티티 클르명, b는 별칭
String jpql = " SELECT b FROM Board b ORDER BY b.id DESC ";
// v1
//em.createNativeQuery()
// Query query = em.createQuery(jpql, Board.class);
// List<Board> boardList = query.getResultList();
// return boardList;
return em.createQuery(jpql, Board.class).getResultList();
}
// 게시글 저장 기능 - 영속성 컨텍스트 활용
@Transactional
public Board save(Board board) {
// v1 -> 네이트브 쿼리를 활용 했다.
// 1. 매개변수로 받은 board는 현재 비영속 상태이다.
// - 아직 영속성 컨텍스트에 관리 되지 않는 상태
// - 데이터베이스와 아직은 연관 없는 순수 Java 객체 상태
// 2. em.persist(board); - 이 엔티티를 영속성 컨텍스트에 저장하는 개념이다.
// - 영속성 컨텍스트가 board 객체를 관리하게 된다.
em.persist(board);
// 3. 트랜잭션 커밋 시점에 Insert 쿼리 실행
// - 이때 영속성 컨텍스트의 변경 사항이 자동으로 DB에 반영 됨
// - board 객체의 id 필드에 자동으로 생성된 값이 설정 됨
// insert ---> DB ---> (pk 값을 알 수 있다)
// select ---> DB ---> (할당된 pk 값 조회)
// 4. 영속 상태로 된 board 객체를 반환
// - 이 시점에는 자동으로 board id 멤버 변수에 db pk 값이 할당된 상태이다
return board;
}
}
선택 기준 정리
조회 유형 | 사용 방법 | 이유 |
기본키 단건 조회 | em.find() | 1차 캐시 최적화, 가장 빠름 |
전체 목록 조회 | JPQL | EntityManager 미지원, JPQL 필수 |
조건부 조회 | JPQL | EntityManager 미지원, JPQL 필수 |
정렬된 조회 | JPQL | ORDER BY 필요,JPQL 필수 |
페이징 조회 | JPQL | setFirstResult/setMaxResults 필요 |
집계 쿼리 | JPQL | COUNT, SUM 등 필요 |
복잡한 조인 | JPQL 또는 Native SQL | 복잡한 로직 필요 |
| 한건에 게시글 조회 단위 테스트, find(),JPQL 만들어 보기
package com.tenco.blog.board;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@RequiredArgsConstructor
@Controller // IoC 대상 - 싱글톤 패턴으로 관리 됨
public class BoardController {
// 생성자 의존 주의 - DI 처리
private final BoardPersistRepository br;
// 게시글 상세 보기
// 주소설계 GET : http://localhost:8080/board/3
@GetMapping("/board/{id}")
public String detail(@PathVariable(name = "id") Long id, HttpServletRequest request) {
Board board = br.findById(id);
request.setAttribute("board", board);
// prefix: classpath:/templates/
// return : board/detail
// suffix: .mustache
// 1차 캐시 효과 - DB에 접근하지 않고 바로 영속성 컨텍스트에서 꺼낸다.
// br.findById(id);
return "board/detail";
}
// 주소 설계 : http://localhost:8080/ , http://localhost:8080/index
@GetMapping({"/", "/index"})
public String boardList(HttpServletRequest request) {
List<Board> boardList = br.findAll();
request.setAttribute("boardList", boardList);
return "index";
}
// 게시글 작성 화면 요청 처리
@GetMapping("/board/save-form")
public String saveForm() {
return "board/save-form";
}
// 게시글 작성 액션(수행) 처리
@PostMapping("/board/save")
public String save(BoardRequest.SaveDTO reqDTO) {
// HTTP 요청 본문 : title=값&content=값&username=값
// form 태그의 MIME 타입 ( application/x-www-form-urlencoded)
// reqDTO <-- 사용자가 던지 데이터 상태가 있음
// DTO -- Board -- DB
//Board board = new Board(reqDTO.getTitle(), reqDTO.getContent(), reqDTO.getUsername());
Board board = reqDTO.toEntity();
br.save(board);
return "redirect:/";
}
}
728x90
'Spring boot' 카테고리의 다른 글
버전-2 -5. 게시글 삭제하기 V2 - Persistence Context와 영속성 관리 (0) | 2025.06.24 |
---|---|
버전 2 -4.게시글 수정하기 V2 - Persistence Context와 Dirty Checking 활용 (0) | 2025.06.24 |
버전 2 -2.게시글 목록 보기 - Persistence Context와 JPQL 활용 (0) | 2025.06.23 |
JPQL 핵심 개념과 대표 예시 코드 (1) (0) | 2025.06.23 |
EntityManager 핵심 메서드 요약표 (0) | 2025.06.23 |