JPA 스프링부트 - 관계 매핑, JPQL, Query DSL

박지명·2026년 5월 10일

스프링부트

목록 보기
6/10

1. 엔티티 관계 매핑

1:1 관계 (OneToOne)

소유 측 (FK를 가진 쪽): UserInfo

@OneToOne
@JoinColumn(name = "id")
private User user;

피소유 측: User

@OneToOne(mappedBy = "user")
private UserInfo userInfo;

조회:

User user = userRepository.findById("hong").get();
user.getUserInfo().getAddress();  // User → UserInfo 접근

1:N 관계 (OneToMany / ManyToOne)

1 쪽: User

@OneToMany(mappedBy = "user")
private List<Board> board;

N 쪽: Board

@ManyToOne
@JoinColumn(name = "id")
private User user;

조회:

User user = userRepository.findById("hong").get();
user.getBoard().size();           // 게시판 개수
user.getBoard().forEach(b -> {
    System.out.println(b.getSubject());
});

N:N 관계 (Many-to-Many)

문제: 직접 N:N 표현 불가 → 중간테이블 필수

Board ←→ Tagging ←→ Tag  (1:N + N:1로 변환)

Board:

@OneToMany(mappedBy = "board")
private List<Tagging> tagging;

Tagging (중간테이블):

@ManyToOne
@JoinColumn(name = "bseq")
private Board board;

@ManyToOne
@JoinColumn(name = "tseq")
private Tag tag;

Tag:

@OneToMany(mappedBy = "tag")
private List<Tagging> tagging;

조회:

Board board = boardRepository.findById(10L).get();
board.getTagging().get(0).getTag().getTag();  // Board → Tagging → Tag

2. JPQL (Java Persistence Query Language)

SQL: 테이블 대상
JPQL: 엔티티 대상 (더 권장)

// JPQL - 엔티티를 대상
@Query(value = "select m from Item m")
List<Item> findAll();
// SQL: select * from tblItem

// Native Query - DB 종속적 (비권장)
@Query(value = "select * from tblItem", nativeQuery = true)
List<Item> findAllNative();

파라미터 전달

// 1개 파라미터
@Query("select m from Item m where m.color = :color")
List<Item> findByColor(@Param("color") String color);

// 객체 파라미터 (SpEL)
@Query("select m from Item m where m.color = :#{#dto.color} and m.price >= :#{#dto.price}")
List<Item> findByCondition(@Param("dto") ItemDto dto);

3. Query DSL

개념

JPQL의 문제점 → Query DSL이 해결

  • 문자열 기반 (JPQL) → 컴파일 오류 감지 불가 X
  • 메서드 기반 (QueryDSL) → 컴파일 오류 감지 가능 V
  • 동적 쿼리 생성 용이

설정

@Configuration
@RequiredArgsConstructor
public class QueryDSLConfig {
    
    private final EntityManager em;
    
    @Bean
    public JPAQueryFactory jPAQueryFactory() {
        return new JPAQueryFactory(em);
    }
}

사용

@Repository
@RequiredArgsConstructor
public class ItemQueryDSLRepository {
    
    private final JPAQueryFactory factory;
    
    // import static com.test.java.entity.QItem.item;
    
    // 전체 조회
    public List<Item> findAll() {
        return factory
                    .selectFrom(item)
                    .fetch();
    }
    
    // 조건 조회
    public Item findByName(String name) {
        return factory
                    .selectFrom(item)
                    .where(item.name.eq(name))
                    .fetchOne();  // 단일행
    }
    
    // 특정 컬럼만
    public List<String> findNames() {
        return factory
                    .select(item.name)
                    .from(item)
                    .fetch();
    }
    
    // 다중 컬럼 (Tuple)
    public List<Tuple> findMultiCol() {
        return factory
                    .select(item.name, item.color, item.qty)
                    .from(item)
                    .fetch();
    }
}

ResultSet 매핑 메서드

메서드반환 타입설명
fetch()List전체 행 (없으면 빈 리스트)
fetchOne()T단일행 (2개 이상이면 예외)
fetchFirst()T첫 행만 (없으면 null)
fetchCount()long행 개수

Query DSL 조건 연산자

.where(item.name.eq("마우스"))           // =
.where(item.price.gt(100000))            // >
.where(item.price.goe(100000))           // >=
.where(item.price.between(50000, 100000))  // between
.where(item.color.in("black", "white"))  // in
.where(item.name.contains("마우"))       // like '%마우%'
.where(item.name.startsWith("마우"))     // like '마우%'
.where(item.qty.isNull())                // is null
.where(item.color.eq("white")
       .and(item.price.gte(100000)))     // AND
.where(item.color.eq("white")
       .or(item.price.gte(100000)))      // OR

4. 3가지 방식 비교

특징Query MethodJPQLQuery DSL
단순성가장 쉬움중간중간
타입 안전성높음낮음높음
컴파일 오류 감지OXO
동적 쿼리불가복잡쉬움
사용 시기단순 조회복잡한 쿼리동적 조건

5. 실무 활용 가이드

단순 조회 (예: where name = ?)
Query Method 사용

복잡한 조인이나 집계함수 (예: select count(*) from ... where ...)
JPQL 사용

동적 조건 (사용자 선택에 따라 AND/OR가 달라짐)
Query DSL 사용

0개의 댓글