JPA(Java Persistence API) 사용법 정리하기

안녕하세요! 이번 글에서는 **JPA(Java Persistence API)**의 핵심 개념과 사용법을 정리해 보려고 합니다. JPA는 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 표준 ORM(Object-Relational Mapping) 기술입니다. JPA를 사용하면 SQL 쿼리를 직접 작성하지 않고도 객체를 데이터베이스에 쉽게 매핑할 수 있어 생산성과 유지보수성이 크게 향상됩니다. ✨


 

1. JPA란 무엇인가?

 

JPA는 자바 ORM 기술의 표준 명세입니다. 즉, JPA 자체는 프레임워크가 아니라 인터페이스의 모음이며, 이 명세를 구현한 대표적인 프레임워크가 바로 Hibernate, EclipseLink, DataNucleus 등입니다. Spring Boot에서는 보통 Spring Data JPA와 함께 Hibernate를 사용합니다.

**ORM(Object-Relational Mapping)**은 객체(Object)와 관계형 데이터베이스(Relational Database)의 데이터를 자동으로 매핑하는 기술을 의미합니다.


 

2. 엔티티(Entity) 정의하기

 

JPA의 가장 기본적인 단위는 엔티티입니다. 엔티티는 데이터베이스 테이블과 매핑되는 자바 클래스를 의미합니다.

  • @Entity: 해당 클래스가 엔티티임을 명시합니다.
  • @Table: 엔티티가 매핑될 데이터베이스 테이블 이름을 지정합니다. (클래스 이름과 테이블 이름이 같으면 생략 가능)
  • @Id: 해당 필드가 테이블의 **기본 키(Primary Key)**임을 지정합니다.
  • @GeneratedValue: 기본 키의 생성 전략을 지정합니다.
    • GenerationType.IDENTITY: 데이터베이스에 기본 키 생성을 위임합니다. (MySQL의 AUTO_INCREMENT)
    • GenerationType.SEQUENCE: 데이터베이스 시퀀스를 사용합니다. (Oracle, PostgreSQL)
    • GenerationType.AUTO: 데이터베이스에 맞게 자동으로 선택합니다.

예시:

Java

import javax.persistence.*;

@Entity
@Table(name = "members")
public class Member {

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

    private String name;

    private int age;

    // 기본 생성자
    public Member() {}

    // 생성자, Getter, Setter ...
}

 

3. JPA 리포지토리(Repository)

 

Spring Data JPA는 인터페이스만으로 데이터베이스 CRUD(Create, Read, Update, Delete) 기능을 제공합니다. JpaRepository 인터페이스를 상속받기만 하면 복잡한 구현 코드 없이 메서드를 사용할 수 있습니다.

JpaRepository<T, ID>에서 T는 엔티티 클래스, ID는 엔티티의 기본 키 타입입니다.

예시:

Java

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {

    // 쿼리 메서드: 이름으로 멤버를 찾는 메서드
    Optional<Member> findByName(String name);

    // 쿼리 메서드: 이름과 나이로 멤버를 찾는 메서드
    List<Member> findByNameAndAge(String name, int age);
}
  • 쿼리 메서드: findBy..., findBy...And...와 같은 규칙에 맞춰 메서드 이름을 작성하면 Spring Data JPA가 자동으로 SQL 쿼리를 생성해줍니다.

 

4. 엔티티 관계 매핑

 

JPA는 객체 간의 관계를 데이터베이스 테이블의 관계로 자동으로 매핑할 수 있습니다. 가장 흔하게 사용되는 관계는 다음과 같습니다.

  • @OneToMany, @ManyToOne: 일대다, 다대일 관계
  • @OneToOne: 일대일 관계
  • @ManyToMany: 다대다 관계

예시: 팀(Team)과 멤버(Member)의 일대다 관계

Java

// Team 엔티티 (일대다 관계의 '일'에 해당)
@Entity
public class Team {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team") // mappedBy는 연관관계의 주인을 지정
    private List<Member> members = new ArrayList<>();
    // ...
}

// Member 엔티티 (일대다 관계의 '다'에 해당)
@Entity
public class Member {
    
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY) // 지연 로딩 설정
    @JoinColumn(name = "team_id") // 외래키 컬럼 지정
    private Team team;
    // ...
}
  • fetch 속성:
    • FetchType.EAGER (즉시 로딩): 연관된 엔티티를 즉시 함께 조회합니다.
    • FetchType.LAZY (지연 로딩): 연관된 엔티티를 실제로 사용할 때 조회합니다. LAZY를 권장합니다.
  • @JoinColumn: 외래 키 컬럼을 지정합니다.

 

5. 트랜잭션(Transaction)

20250911 YD 31

JPA에서 데이터 변경(등록, 수정, 삭제)은 항상 트랜잭션 안에서 이루어져야 합니다. Spring Boot에서는 @Transactional 어노테이션을 사용해 쉽게 트랜잭션을 적용할 수 있습니다.

Java

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Transactional
    public void createMember(Member member) {
        memberRepository.save(member);
    }
}

@Transactional은 메서드 실행을 하나의 트랜잭션으로 묶어줍니다. 메서드 실행 중 예외가 발생하면 모든 변경 사항이 롤백됩니다.


 

마치며

 

JPA는 객체지향적인 방식으로 데이터를 관리할 수 있게 해주는 강력한 도구입니다. 복잡한 SQL 쿼리 대신 메서드 호출과 어노테이션을 사용하여 데이터베이스와 상호작용함으로써 개발 생산성을 크게 높일 수 있습니다. 위에서 정리한 기본적인 사용법을 숙지하고, 실제 프로젝트에 적용해 보면서 JPA의 편리함을 직접 느껴보시길 바랍니다! 😊