Engineering Note

[JPA] JPA에서 연관관계가 맺어진 객체를 비교(eq)하는 것은, 실제 DB에서는 그 객체의 'ID(PK) 값'을 비교하는 것과 완전히 동일하다. 본문

Server/JPA ORM

[JPA] JPA에서 연관관계가 맺어진 객체를 비교(eq)하는 것은, 실제 DB에서는 그 객체의 'ID(PK) 값'을 비교하는 것과 완전히 동일하다.

Software Engineer Kim 2026. 1. 14. 14:42

JPA, QueryDsl 에서는 객체비교, ID(PK)비교 결과적으로 같은 SQL을 만든다.

객체 비교: itemCategory.item.eq(item)

ID 비교: itemCategory.item.id.eq(item.getId())

 

둘 다 SQL로 변환되면? ➔ WHERE ic.item_id = 101

 

 

JPA는 똑똑하게도 객체(인스턴스)를 통째로 넣으면, **"아, 이 객체의 식별자(PK) 값을 꺼내서 비교하라는 거구나!"**라고 알아서 해석해 줍니다. 그래서 인스턴스를 직접 넣어도 내부적으로는 ID 값이 비교되는 것입니다.

 

 

예시

상품 검색 조건에 따라 필터링해서 상품을 조회하는 동적쿼리에서 카테고리를 필터링하는 categoryFilter(Long categoryId) 메서드를 보면, 

'itemCategory.item.eq(item).and(itemCategory.category.id.eq(categoryId))' 라는 서브쿼리가 있는데 'and' 앞쪽은 객체 인스턴스를 직접 비교하면서 Entity값의 일치성을 확인했고, 'and'의 뒤 쿼리는 'Long id PK값'으로 비교하면서 두 데이터의 일치성을 확인했지만, 결과적으로 SQL이 실행될 때는 모두 PK값을 비교하는 SQL문이 실행됩니다.

 

@Override
public Page<Item> findBySearchOption(ItemSearchDto itemSearchDto, Pageable pageable) {

  // 1. 데이터 조회 Item만 페이지네이션 적용
  List<Item> content =
      jpaQueryFactory
          .selectFrom(item)
          .where(
              itemNameStartsWith(itemSearchDto.getItemName()),
              categoryFilter(itemSearchDto.getCategoryId()),
              isSellOrSoldOut(ItemSellStatus.SELL))
          .offset(pageable.getOffset())
          .limit(pageable.getPageSize())
          .orderBy(item.regTime.desc())
          .fetch();


  return PageableExecutionUtils.getPage(content, pageable);
}


// 카테고리 필터링 조건 (서브쿼리 방식)
private BooleanExpression categoryFilter(Long categoryId) {
  if (categoryId == null) {
    return null;
  }
  return JPAExpressions.selectFrom(itemCategory)
      .where(
          itemCategory
              .item
              .eq(item) // 외부 쿼리의 item과 연결
              .and(itemCategory.category.id.eq(categoryId)))
      .exists();
}
Comments