Spring/강의

[📕 JPA 심화] 3. RawJPA 기본

가지코딩 2025. 6. 26. 14:44

📕 목차

  1. 쿼리 파일 만들기 (QueryMapper)
  2. 쿼리 코드 만들기 (JpaRepository)
  3. 테이블 객체 이해하기
  4. 테이블 객체 만들기 & 테이블 객체끼지 관계 만들기

❤️ 학습 목표

  • Query Mapper를 사용하여 SQL 쿼리를 관리하는 방법을 배웁니다. 별도의 쿼리 파일을 통해 SQL을 관리함으로써 코드의 가독성과 유지보수성을 높입니다.
  • Annotation을 사용하여 SQL 쿼리를 클래스나 메소드에 직접 정의하는 방법을 배웁니다. 이를 통해 동적 쿼리 생성과 실행 과정을 이해하고 실습합니다.
  • RawJPA를 사용하여 테이블과 연동되는 객체(엔티티)의 구조와 작동 원리를 배웁니다. JPA의 기본적인 구성 요소와 엔티티의 생명주기를 이해합니다.
  • 실제 데이터베이스 테이블과 매핑되는 Java 객체를 생성하는 방법을 배웁니다. 엔티티 클래스 작성과 어노테이션을 통한 필드 매핑 방법을 실습합니다.
  • 엔티티 간의 관계(일대일, 일대다, 다대다)를 정의하고 매핑하는 방법을 배웁니다. 연관 관계 매핑을 통해 복잡한 데이터 구조를 효과적으로 관리하는 방법을 학습합니다.

1. 쿼리 파일 만들기 (QueryMapper)

MyBatis 란?

  • JDBC 반복 코드를 줄이고, 비즈니스 코드와 SQL을 분리하여 관리할 수 있게 만든 SQL 매퍼 프레임워크.
  • 주요 특징
    • SQL을 XML이나 어노테이션에 작성하여 코드와 분리
    • Mapper 인터페이스와 SQL 매핑으로 쿼리 실행
    • 복잡한 JDBC 설정 간소화
    • SQL에 집중하여 빠른 개발 가능
  • 한계점
    • SQL 직접 작성으로 DB 벤더에 종속적
    • 테이블별 CRUD 코드 반복 발생
    • DB 구조에 밀접하게 의존함

 

 

MyBatis 의 동작

 

  • SqlSessionFactoryBuilder: 설정 파일을 읽어 SqlSessionFactory 생성
  • MyBatis 설정 파일: 매핑할 도메인과 쿼리 XML 파일 경로 지정
  • SqlSessionFactory: SqlSession 객체를 만드는 공장
  • SqlSession: SQL 실행과 결과 처리를 담당하는 세션
  • Mapper 인터페이스
    • 방법1: SqlSession 직접 사용 (쿼리 실행 시 네임스페이스와 id 필요)
    • 방법2: Mapper 인터페이스 + XML 매핑 (자동 호출, 메소드명 = 쿼리 id)
  • Mapper XML 파일: SQL 쿼리 정의 및 Mapper 인터페이스와 연결

 

 


2. 쿼리 코드 만들기 (JpaRepository)

ORM(객체-관계 매핑)이란?

  • 객체(Object)테이블(RDB)을 자동으로 매핑해주는 기술
  • SQL 직접 작성 없이 자바 코드로 DB 조작 가능
  • 반복 SQL 제거, 객체지향 프로그래밍과의 불일치 해결

 

ORM이 해결한 문제들

문제 설명 해결 방법
상속 테이블 간 상속 없음 @OneToMany, @ManyToOne
관계 외래키 기반의 방향성 없음 @JoinColumn, @MappedBy
탐색 객체 탐색은 순차적, RDB는 JOIN @FetchType, fetchJoin()
밀도 객체는 복잡, 테이블은 단순 @Embedded
식별 객체: equals/hash, RDB: PK @Id, @GeneratedValue

 

 

영속성 컨텍스트 (1차 캐시)

 

  • EntityManager가 관리하는 엔티티 저장소
  • 쓰기 지연(Write-behind): persist() 후 SQL은 즉시 실행되지 않음 → flush() 또는 commit() 시 실행됨
  • 이 덕분에 SQL 최소화 및 성능 최적화 가능
em.persist(entity); // INSERT는 바로 안 됨
transaction.commit(); // 이 시점에 INSERT 수행

 

 

 

Repository vs JpaRepository

항목 Repository JpaRepository
사용 방식 클래스 직접 구현 + @Repository 인터페이스 상속만 하면 됨
EntityManager 사용 직접 사용 내부적으로 자동 사용
기능 기본 CRUD CRUD + 페이징, 정렬 등 확장 기능
개발 편의성 낮음 매우 높음

 

 

JpaRepository 예시

public interface UserRepository extends JpaRepository<User, Long> {
  // 기본 CRUD 자동 생성됨
}

3. 테이블 객체 이해하기

JPA 주요 매핑 어노테이션

  • @Entity: 객체를 엔티티로 지정 (JQL용 이름)
  • @Table: DB 테이블 이름 지정 (SQL용)
  • @Id, @GeneratedValue: 기본키, 생성 전략
  • @Column: 컬럼 속성 지정 (nullable, length 등)
  • @Transient: DB에 매핑되지 않음
  • @Temporal: Date/Calendar 타입 매핑

 

필드 타입 매핑

 

  • 기본 타입
    • @Column: 필드 속성 지정
    • @Enumerated(EnumType.STRING): enum 매핑
  • 복합 값 타입
    • @Embeddable: 내장 객체 클래스
    • @Embedded: 내장 객체 사용
    • @AttributeOverride(s): 내장 객체 컬럼 이름 재정의
  • 컬렉션 값 타입
    • @ElementCollection: 기본/복합 값 타입 컬렉션 매핑
    • 실무에서는 복합 값보다는 연관관계를 사용한다.

 

연관 관계 매핑

관계 유형 설명 사용 예시
@ManyToOne 다대일(N:1) 관계 댓글 → 게시글
@OneToMany 일대다(1:N) 관계 게시글 → 댓글
@OneToOne 일대일(1:1) 관계 사용자 ↔ 프로필
@ManyToMany 다대다(N:N) 관계 사용자 ↔ 채널

 

 


4. 테이블 객체 만들기 & 테이블 객체끼지 관계 만들기

슬랙(Slack 메신저) 도메인

  • User: 채널과만 양방향 관계. 나머지 도메인과는 단방향 관계.
  • Channel: 유저와 다대다 관계.
  • Thread: 채널 내부 대화 쓰레드. 댓글, 이모지, 멘션과 관계.
  • Comment: Thread와 다대일. Emotion, Mention과 관계.
  • Emotion / Mention: Thread, Comment와 다대다 관계.

 

User 엔티티 예제

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // User ↔ Channel (ManyToMany)
    @ManyToMany
    @JoinTable(
        name = "user_channel",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "channel_id")
    )
    private List<Channel> channels = new ArrayList<>();
}

 

 

Channel 엔티티 예제

@Entity
@Table(name = "channels")
public class Channel {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Channel ↔ User (ManyToMany, 양방향)
    @ManyToMany(mappedBy = "channels")
    private List<User> users = new ArrayList<>();

    // Channel → Thread (OneToMany)
    @OneToMany(mappedBy = "channel", cascade = CascadeType.ALL)
    private List<Thread> threads = new ArrayList<>();
}

 

 

Thread 엔티티 예제

@Entity
@Table(name = "threads")
public class Thread {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String content;

    // Thread → Channel (ManyToOne)
    @ManyToOne
    @JoinColumn(name = "channel_id")
    private Channel channel;

    // 추후: 댓글(Comment), 이모지(Emotion), 멘션(Mention)도 매핑 가능
}