스프링 부트와 JPA

ppp·2025년 7월 11일

ORM

  • ORM은 Object-Relational Mapping의 약어로, 객체(Object)와 관계형 데이터베이스(Relational Database) 사이의 데이터를 자동으로 매핑해주는 기술이다.

  • 자바의 객체와 관계형 데이터베이스의 테이블 사이의 구조적 차이를 매핑하여, 개발자가 SQL을 직접 작성하지 않고도 데이터베이스를 조작할 수 있도록 지원한다.

  • 즉, 객체 지향 프로그래밍 언어로만 데이터베이스를 다룰 수 있도록 도와주는 추상화 도구이다.

핵심 개념

구분설명
객체(Object)자바 등 OOP 언어에서 사용하는 클래스 기반의 데이터 구조
관계형 테이블(Relational Table)DB에서 데이터를 행(row)과 열(column)로 표현하는 구조
매핑(Mapping)클래스 ↔ 테이블, 필드 ↔ 컬럼, 객체 ↔ 레코드 간 변환 작업

장점

  • 비즈니스 로직에 집중할 수 있음 (SQL 분리)

  • 생산성 향상 및 유지보수 용이

  • 객체지향적 설계와 데이터 접근 방식의 일관성 유지

  • DBMS에 대한 의존도 감소 (DB 추상화)

단점

  • 복잡한 SQL 쿼리 처리에는 한계가 있음 (성능 이슈)

  • 예기치 않은 SQL이 생성될 수 있음 (튜닝 어려움)

JPA와 하이버네이트

  • ORM에는 여러 종류가 있다. 자바에서는 JPA(java persistence API)를 표준으로 사용한다.

  • JPA는 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다. 인터페이스이므로 실제 사용을 위해서는 ORM 프레임워크를 추가로 선택해야 한다.

  • 하이버네이트는 JPA 인터페이스를 구현한 구현체이자 자바용 ORM 프레임워크이다. 내부적으로는 JDBC API를 사용한다.

  • 애플리케이션 ↔ 스프링 데이터 JPA ↔ JPA ↔ 하이버네이트 ↔ JDBC ↔ 데이터베이스

엔티티

  • 데이터베이스의 테이블과 매핑되는 객체를 의미

  • 자바의 일반 객체와 다르지 않지만 데이터베이스에 영향을 미치는 쿼리를 실행하는 객체

엔티티 매니저

  • 엔티티를 관리해 데이터베이스와 애플리케이션 사이에서 객체를 생성, 수정, 삭제하는 등의 역할을 한다.

  • 엔티티 매니저를 만드는 곳은 엔티티 매니저 팩토리

  • 스프링 부트는 내부에서 엔티티 매니저 팩토리를 하나만 생성해서 관리하고 @PersistenceContext 또는 @Autowired 애너테이션을 사용해서 엔티티 매니저를 사용한다.

@PersistenceContext
EntityManager em; // 프록시 엔티티 매니저
  • 스프링 부트는 기본적으로 빈을 하나만 생성해서 공유하므로 동시성 문제가 발생할 수 있다. 그래서 실제로는 엔티티 매니저가 아닌 실제 엔티티 매니저와 연결하는 프록시(가짜) 엔티티 매니저를 사용한다. 그리고 필요할 때 데이터베이스 트랙잭션과 관련된 실제 엔티티 매니저를 호출한다.

  • 엔티티 매니저는 Spring Data JPA에서 관리

엔티티의 상태

  1. 영속성 컨텍스트가 관리하고 있지 않는 분리(detached) 상태
  2. 관리하는 관리(managed) 상태
  3. 영속성 컨텍스트와 전혀 관계가 없는 비영속(transient) 상태
  4. 삭제된(removed) 상태

특정 메서드를 호출해 상태는 변경 가능하다. 필요에 따라 엔티티의 상태를 조절해 데이터를 올바르게 유지하고 관리할 수 있다. 엔티티를 처음 만들면 엔티티는 비영속 상태가 된다.

public class EntityManagerTest {

  @Autowired
  EntityManager em;
  
  public void example() {
    // 비영속 상태
    Member member = new Member(1L, "홍길동");
    
    // 관리 상태
    em.persist(member);
    // 분리 상태
    em.detach(member);
    // 삭제 상태
    em.remove(member);
  }
}

영속성 컨텍스트

  • 엔티티 매니저는 엔티티를 영속성 컨텍스트에 저장한다는 특징이 있다.

  • 영속성 컨텍스트는 JPA의 중요한 특징 중 하나로, 엔티티를 관리하는 가상의 공간이다.

  • 데이터베이스의 접근을 최소화해 성능을 높일 수 있다.

1차 캐시

  • 영속성 컨텍스트는 내부에 1차 캐시를 가지고 있다. 이때 캐시의 키는 엔티티의 @Id 애너테이션이 달린 기본키 역할을 하는 식별자이며 값은 엔티티이다.

  • 엔티티를 조회하면 1차 캐시에서 데이터를 조회하고 값이 있으면 반환한다. 값이 없으면 데이터베이스에서 조회해 1차 캐시에 저장한 다음 반환한다.

쓰기 지연

  • 쿼리를 모았다가 트랜잭션을 커밋하면 모았던 쿼리를 한번에 실행하는 것을 의미한다.

  • 적당한 묶음으로 쿼리를 요청할 수 있어 데이터베이스 시스템의 부담을 줄일 수 있다.

변경 감지

  • 트랜잭션을 커밋하면 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서 변경된 값이 있다면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영한다.

지연 로딩

  • 쿼리로 요청한 데이터를 애플리케이션에 바로 로딩하는 것이 아니라 필요할 때 쿼리를 날려 데이터를 조회하는 것을 의미한다.

  • 반대로 조회할 때 쿼리를 보내 연관된 모든 데이터를 가져오는 즉시 로딩도 있다.

스프링 데이터

  • 스프링 데이터는 비즈니스 로직에 더 집중할 수 있게 데이터베이스 사용 기능을 클래스 레벨에서 추상화했다.

  • 스프링 데이터에서 제공하는 인터페이스를 통해서 스프링 데이터를 사용할 수 있다. CRUD 포함 여러 메서드, 페이징 처리 기능, 쿼리 메서드 등을 제공

  • spring data JPA, spring data MongoDB 등 데이터베이스의 특성에 맞춰 기능을 확장해 제공하는 기술도 제공한다.

스프링 데이터 JPA

  • 스프링 데이터 JPA는 스프링 데이터의 공통적인 기능에서 JPA의 유용한 기술이 추가된 기술이다.

  • 스프링 데이터의 인터페이스인 PagingAndSortingRepository를 상속받아 JpaRepository 인터페이스를 만들었고 JPA를 더 편리하게 사용하는 메서드를 제공한다.

  • 스프링 데이터 JPA를 사용하면 리포지토리 역할을 하는 인터페이스를 만들어 데이터베이스의 테이블 조회, 수정, 생성, 삭제 같은 작업을 간단히 할 수 있다.

  • JpaRepository 인터페이스를 우리가 만든 인터페이스에 상속받고, 제네릭에는 관리할 <엔티티 이름, 엔티티 기본키의 타입>을 작성하면 기본 CRUD 메서드를 사용할 수 있다.

public interface MemberRepository extends JpaRepository<Member, Long> {
}

쿼리 메서드

  • JPA는 메서드 이름으로 쿼리를 작성하는 기능을 제공

  • 쿼리 메서드는 JPA가 정해준 메서드 이름 규칙을 따르면 쿼리문을 특별히 구현하지 않아도 메서드처럼 사용할 수 있다.

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
  Optional<Member> findByName(String name);
}

update

  • JPA는 트랜잭션 내에서 데이터를 수정해야 한다. 따라서 데이터를 수정할 때는 그냥 메서드만 사용하면 안 되고 @Transactional 애너테이션을 메서드에 추가해야 한다.
public class Member {
  ...
  public void changeName(String name) {
    this.name = name;
  }
}
  • 위 메서드가 @Transactional 애너테이션이 포함된 메서드에서 호출되면 JPA는 변경 감지 기능을 통해 엔티티의 필드값이 변경될 때 그 변경 사항을 데이터베이스에 자동으로 반영한다.

0개의 댓글