JPA

조승빈·2024년 11월 21일

Spring DB

목록 보기
4/8

ORM?

ORM(Object-Relational Mapping)객체와 관계형 데이터베이스를 매핑해주는 기술이다.
즉, 객체를 관계형 데이터베이스에 저장하거나 조회할 때 SQL을 직접 작성하지 않고도 객체와 테이블 간의 데이터를 자동으로 변환해주는 역할을 한다.

SQL 중심 개발의 문제점

패러다임의 불일치

객체는 객체지향 개념을 따르지만, 데이터베이스는 관계형 모델을 기반으로 동작한다.
이 차이로 인해 객체를 테이블에 저장하려면 개발자가 직접 객체 → SQL 변환 작업을 해야 한다.

수정의 어려움

예를 들어, 데이터베이스에 새로운 칼럼을 추가했다고 가정하자.
모든 SQL 문을 수정해야 하는 번거로움이 생긴다.

객체 탐색 제약

객체는 필드와 메서드로 자유롭게 연관된 데이터를 탐색할 수 있다.
하지만 SQL은 작성된 쿼리에 따라 탐색 범위가 제한된다.

비효율적 작업 증가

객체답게 모델링할수록 객체와 데이터베이스 간의 변환 작업이 늘어난다.

ORM의 해결책: JPA

JPA(Java Persistence API)는 객체를 자바 컬렉션에 저장하듯이 데이터베이스에 저장할 수 있도록 도와주는 기술 표준이다.
즉, JPA를 사용하면 객체와 데이터베이스 간 변환 작업을 자동으로 처리할 수 있다.

JPA는 애플리케이션과 JDBC 사이에서 동작한다.


JPA 의 장점

1차 캐시와 동일성 보장

  • 같은 트랜잭션 안에서는 같은 엔티티를 반환 -> 조회 성능 약간 향상

  • 트랜잭션을 커밋할 때까지 insert sql을 모음
    jdbc batch sql기능을 사용해서 한번에 전송
    트랜잭션을 커밋하기 전까지 데이터베이스에 반영되지 않고 커밋하는 순간 데이터베이스에 insert sql을 보낸다.

지연로딩과 즉시 로딩을 지원

지연로딩 : 객체가 실제 사용될 때 로딩
즉시로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회

객체와 JPA의 매핑 예시

엔티티 클래스
엔티티는 데이터베이스 테이블과 매핑되는 자바 클래스이다.
JPA는 엔티티 클래스의 필드와 테이블의 칼럼을 매핑해준다.

import
 jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public class User {
    
    @Id
    private Long id; // Primary Key

    private String name;
    private String email;

    // Getter, Setter, Constructor
}

@Entity: 이 클래스가 데이터베이스 테이블과 매핑된다는 것을 JPA에 알린다.

ORM의 장점

- 생산성
반복적인 SQL 작성 없이 객체를 저장하거나 조회할 수 있어 개발 속도가 빨라진다.
- 유지보수
엔티티 클래스만 수정하면 데이터베이스와의 매핑이 자동으로 업데이트되므로 SQL 수정 작업이 줄어든다.
- 객체지향적인 설계
객체 간의 관계를 자유롭게 탐색할 수 있어 객체지향적으로 데이터를 다룰 수 있다.
- 데이터베이스 독립성
JPA는 특정 데이터베이스에 종속되지 않으므로 데이터베이스를 변경해도 코드 수정이 최소화된다.


JPA 핵심 개념 및 활용 방법

1. 모든 데이터 변경은 트랜잭션 안에서 이루어져야 한다

  • JPA는 데이터 변경 작업(저장, 수정, 삭제 등)을 트랜잭션 내에서 실행해야 함.
  • 트랜잭션 없이 데이터를 변경하면 제대로 반영되지 않거나 오류가 발생할 수 있음.

2. EntityManager를 통한 데이터 조작

  • JPA는 데이터를 조작하기 위해 EntityManager를 사용.
  • 주요 메서드:
    • 저장: em.persist(entity)
      엔티티를 영속성 컨텍스트에 저장하여 DB에 INSERT 쿼리를 실행.
    • 수정:
      1. 수정할 엔티티를 조회.
      2. 수정할 필드를 set 메서드로 변경.
      MyEntity entity = em.find(MyEntity.class, id);
      entity.setName("newName");
      • 별도의 em.update() 메서드는 없음.
        엔티티의 변경 사항은 JPA가 자동으로 감지하여 UPDATE 쿼리를 실행.

3. JPA의 핵심: 영속성 컨텍스트

  • 영속성 컨텍스트는 엔티티를 1차 캐시에 저장하고 변경 사항을 추적.
  • 변경 감지(Dirty Checking) 덕분에 개발자는 별도의 update 메서드 호출 없이 엔티티만 수정하면 됨.
    @Transactional
    public void updateEntity(Long id) {
        MyEntity entity = em.find(MyEntity.class, id); // 영속 상태
        entity.setName("Updated Name"); // 변경 감지
        // 트랜잭션 종료 시 UPDATE 쿼리 자동 실행
    }

4. 복잡한 조회: JPQL

  • SQL: 테이블을 대상으로 작업.
  • JPQL: 엔티티 객체를 대상으로 작업.
    SQL과 비슷하지만 엔티티 및 객체 필드를 기반으로 작성.
String jpql = "SELECT e FROM Employee e WHERE e.name = :name";
List<Employee> result = em.createQuery(jpql, Employee.class)
                          .setParameter("name", "John")
                          .getResultList();
  • 동적 쿼리의 경우 JPQL은 코드가 복잡해질 수 있음.

    이를 해결하기 위해 QueryDSL과 같은 기술을 사용하면 더 깔끔하고 가독성 높은 코드 작성 가능.

5. JPA 예외 처리: 스프링 예외 추상화

JPA는 순수 자바 기반 기술이므로 JPA 자체에서 발생하는 예외는 표준 자바 예외와 다르다.
스프링에서는 JPA 예외를 스프링의 예외 추상화 계층으로 변환해 준다.
이 변환은 @Repository를 통해 자동으로 적용된다.

  • @Repository가 붙은 클래스:
    JPA 관련 예외를 스프링의 예외(DataAccessException)로 변환.
    예외 변환은 스프링 AOP를 통해 이루어짐.

6. 예외 처리 예시

예외 발생 시, JPA 예외는 스프링 예외로 변환되어 처리된다.

@Repository
public class MyRepository {
    @PersistenceContext
    private EntityManager em;

    public void saveEntity(MyEntity entity) {
        try {
            em.persist(entity);
        } catch (Exception e) {
            throw new RuntimeException("Failed to save entity", e);
        }
    }
}
profile
평범

0개의 댓글