
BoardRepository
@RequiredArgsConstructor
@Repository
public class BoardRepository {
private final EntityManager em;
public List<Board> findAll() {
Query query = em.createQuery("select b from Board b order by b.id desc", Board.class);
return query.getResultList();
}
Test 해보기!
ORM이라 User객체가 같이 튀어나올 수 도 있으니 테스트 해줘야함!
JPA는 무조건! 쿼리 실행 확인해봐야함! 예상치 못한게 튀어나올 수도 있으니!

스니펫 설정 해줬음
@Test
public void findAll_test() {
//given
//when
List<Board> boardList = boardRepository.findAll();
//then
}
LAZY와 EAGER 비교
[ EAGER ver. ]


EAGER 전략으로 findAll을 했을 때!
findById 일 때는 join을 해서 들고 왔는데, findAll 일 때는 Board만 조회한 뒤에
User가 게시글을 쓴 수만큼 select 절이 더 발생한다
즉,
3명의 user가 글을 적었기 때문에 user join이 3번 되는 것. → 쓰지 말아야겠죠?
더미 데이터를 보면 4개가 있는데 왜 4번이 아닐까? 1번은 캐싱 되기 때문!
컬렉션 조회 시 EAGER은 악(惡) !!!
글 쓴 유저가 많으니까 SELECT가 남발된다 → 직접 JOIN쿼리를 써라 (쌤은 제일 추천!)
[ 만약, 1번인 ssar만 더미 데이터에 있다면? ]


board와 user 각각 1번씩 select가 날아간다. 그러니 총 쿼리문은 1번만 날아가는 것!
[ LAZY ver. ]

똑같이 했는데, fetch전략을 LAZY로 바꾸니까 Board만 조회 됨!
default_batch_fetch_size ?
[ lazy loading 테스트 ]
@Test
public void findAll_lazyloading_test() {
List<Board> boardList = boardRepository.findAll();
boardList.forEach(board -> {
System.out.println(board.getUser().getUsername()); //lazy loading
});
}
boardList.forEach(board -> { ... }); 이 부분에서, board는 boardList에 포함된 각각의 'Board' 객체를 차례로 가리키는 변수. 즉, forEach 루프가 반복될 때마다 board는 리스트의 다음 'Board' 객체를 참조하게 된다. 이를 통해 개별 'Board' 객체에 접근하여 추가적인 작업을 수행할 수 있다.
Board를 타고 들어가서(?) User의 Username을 조회한다 → lazy loading
'User' 객체는 board.getUser()가 호출되는 시점에 데이터베이스에서 로드 됨!
처음 'Board' 엔티티 로드:
데이터베이스에서 'Board' 엔티티를 조회할 때, 연결된 'User' 엔티티는 즉시 로드되지 않는다. 이 시점에서는 'Board' 엔티티만 메모리에 로드됨.
'User' 엔티티에 접근 시 'Lazy Loading' 발생:
'Board' 엔티티로부터 'User' 엔티티의 정보(예: 'Username')가 필요할 때, 그제서야 'User' 엔티티를 데이터베이스에서 로드한다. 이 과정에서 'Lazy Loading'이 발생하며, 실제로 'User' 엔티티의 데이터가 필요한 시점까지 데이터베이스 조회를 지연시킨다.
Hibernate: select b1_0.id, b1_0.content, b1_0.created_at, b1_0.title, b1_0.user_id from board_tb b1_0 order by b1_0.id desc Hibernate: select u1_0.id, u1_0.created_at, u1_0.email, u1_0.password, u1_0.username from user_tb u1_0 where u1_0.id=? love Hibernate: select u1_0.id, u1_0.created_at, u1_0.email, u1_0.password, u1_0.username from user_tb u1_0 where u1_0.id=? cos Hibernate: select u1_0.id, u1_0.created_at, u1_0.email, u1_0.password, u1_0.username from user_tb u1_0 where u1_0.id=? ssar ssar
EAGER이랑 똑같네 (?) → JOIN 써야겠죠
JOIN 외의 해결 방법 [ default_batch_fetch_size ] → 실무/프로젝트는 이걸 사용


LAZY하게 땡길 때, SELECT를 3번 안 땡기고 자동으로 in 쿼리를 만들어줌!
-> SELECT 2방에 끝남!
default_batch_fetch_size = 10 이라고 했으니 10번의 [ ? ] 가 튀어나온 것!
데이터베이스 최적화 기법 중 하나인 IN 절을 이용한 쿼리 최적화 방법
Hibernate 같은 ORM을 사용할 때, default_batch_fetch_size 설정을 통해
한 번에 가져올 연관된 엔티티의 수를 제어하는 방법!
default_batch_fetch_size = 10이라고 설정하면,
한 번의 쿼리로 최대 10개의 연관된 엔티티를 가져올 수 있게 된다.
이는 N+1 문제를 해결하는 데 도움이 되는 설정.
(N+1 문제란, 한 번의 쿼리로 N개의 엔티티를 가져온 후,
각 엔티티에 대해 추가적으로 1번씩 쿼리를 더 날려야 하는 문제)
예를 들어, 게시글을 쓴 사람이 30명이고, 이 사람들의 정보를 가져오고 싶다면,
IN 쿼리에는 한 번에 10명의 user_id가 들어가게 된다.
따라서, 30명의 정보를 가져오기 위해 총 3번의 쿼리가 실행되겠죠.
만약 게시글을 쓴 사람이 3명이라면, 한 번의 쿼리로 충분하고,
10명이라면 역시 한 번의 쿼리로 충분!
WHERE 절에서는 주로 기본키(PK)로 조회하기 때문에
인덱스를 효율적으로 사용할 수 있어 쿼리 성능이 좋다. -> 빠르다!
JOIN보다 IN 쿼리가 더 빠르다!
-> IN 쿼리가 인덱스를 효율적으로 활용할 수 있기 때문!
또한
user_id를 리스트 형태로 동적으로 생성하여 IN 쿼리에 사용하게 되는데,
이는 여러 user_id에 대한 정보를 한 번에 가져올 수 있게 하여
쿼리의 수를 줄이고 성능을 향상시키는 방법!
즉, user_id를 int LIST 같은 걸로 1, 2, 3 ~~ user_id를 모음.
이걸 동적으로 우리가 만들어야 하는 것
BoardController
@GetMapping("/" )
public String index(HttpServletRequest request) {
List<Board> boardList = boardRepository.findAll();
request.setAttribute("boardList", boardList);
return "index";
}
index.mustache
{{#boardList}}
<div class="card mb-3">
<div class="card-body">
<h4 class="card-title mb-3">{{title}}</h4>
<a href="/board/{{id}}" class="btn btn-primary">상세보기</a>
</div>
</div>
{{/boardList}}
화면 확인

목록이 생겼다
TIP!
"select b, (select count(r) from Reply r where r.board.id = b.id)
from Board b order by b.id desc"
서브쿼리는 이런식으로 작성한다~!
[ Pageable ]

페이징도 이런 걸 쓰면 첫 페이지부터 마지막 페이지까지 촤아악 해줌
[ + + 보단 replace를 주로 사용 ]
String q = "seelct * from user where id = :id and username = :username";
q = q.replace(":id", id+"").replace(":username", username+"");
Share article