9osari.log
← All posts

[JPA] JAVA ORM 표준 JPA

JPA(Java Persistence API)

SQL 없이 객체를 데이터베이스에 직접 저장할 수 있게 하고, 객체와 관계형 데이터베이스를 명확히 구분해준다. 즉, 중간에서 매핑 역할을 수행하는 ORM(Object-Relational Mapping) 기술의 표준 인터페이스이다.

객체지향과 관계형 데이터베이스의 패러다임 불일치를 해결해준다.

JPA를 사용하는 이유

유지보수 용이

생산성 향상

객체지향적 개발 가능

데이터베이스 독립성

패러다임 불일치 해결

성능 최적화

SQL 대신 객체로 작업하면서 생산성을 높이고, 유지보수를 쉽게 하기 위해 사용한다.


ORM(Object-Relational Mapping)

객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 기술, 객체지향 프로그래밍의 객체와 관계형 데이터베이스의 테이블 사이의 불일치를 해결해주는 다리 역할을 한다.

객체(Object) ↔ 테이블(Table)

필드(Field) ↔ 컬럼(Column)

인스턴스(Instance) ↔ 행(Row)

JPA는 ‘무엇을 해야 하는지(What)’ , Hibernate 같은 구현체가 ‘어떻게 할 것인가(How)’ 를 구현


JPA의 구동 방식

JPA는 persistence.xml 설정 파일을 읽어서 EntityManagerFactory를 생성한다.

persistence.xml 읽기

EntityManagerFactory 생성 (앱 전체에서 1개만)

EntityManager 생성 (요청마다 생성/삭제)

트랜잭션 단위로 작업 수행

EntityManagerFactory

EntityManager

사용 흐름

// 1. EntityManagerFactory 생성 (앱 시작 시 1번)
EntityManagerFactory emf = 
    Persistence.createEntityManagerFactory("hello");

// 2. EntityManager 생성 (요청마다)
EntityManager em = emf.createEntityManager();

// 3. 트랜잭션 획득
EntityTransaction tx = em.getTransaction();
tx.begin();

try {
    // 비즈니스 로직 실행
    Cat cat = new Cat();
		cat.setId(1L);
		cat.setCatname("생강이");
    
    tx.commit();  // 커밋
} catch (Exception e) {
    tx.rollback();  // 롤백
} finally {
    em.close();  // EntityManager 종료
}

emf.close();  // 앱 종료 시

영속성 컨텍스트

엔티티를 영구 저장하는 환경

em.persist(entity); 는 엔티티를 DB에 저장하는게 아니라, 영속성 컨텍스트에 저장하는 것이다.

엔티티의 생명주기

비영속 (new/transient)

Cat cat = new Cat();
cat.setId(1L);
cat.setCatname("생강이");
// 아직 영속성 컨텍스트에 없음

영속 (managed)

em.persist(cat); //영속성 컨텍스트에 저장됨 (아직 데이터베이스에 저장 안됨)

준영속 (detached)

em.detach(cat);  // 특정 엔티티만 준영속 상태로
em.clear();         // 영속성 컨텍스트 완전히 초기화
em.close();         // 영속성 컨텍스트 종료

삭제 (removed)

em.remove(cat);  //객체를 삭제한 상태

영속성 컨텍스트의 장점

1차 캐시

// 1차 캐시에 저장
Cat cat = new Cat();
cat.setId(1L);
cat.setCatname("생강이");
// 아직 영속성 컨텍스트에 없음

// 1차 캐시에서 조회 (select sql 실행안됨)
Cat findCat = em.find(Cat.class, 1L);

동일성(identity) 보장

Cat a = em.find(Cat.class, 1L);
Cat b = em.find(Cat.class, 1L);

System.out.println(a == b);  // true

트랜잭션을 지원하는 쓰기 지연

EntityTransaction tx = em.getTransaction();
tx.begin();

em.persist(catA);
em.persist(catB);
// 여기까지 INSERT SQL을 DB에 보내지 않음

tx.commit();  // 커밋하는 순간 DB에 INSERT SQL을 모아서 보냄

변경 감지 (Dirty Checking)

EntityTransaction tx = em.getTransaction();
tx.begin();

// 영속 엔티티 조회
Cat catA = em.find(Cat.class, 1L);

// 영속 엔티티 데이터 수정
catA.setCatname("여름이");
catA.setAge(3);

// em.update(member) 이런 코드 필요 없음!

tx.commit();  // 커밋 시점에 자동으로 UPDATE SQL 실행

지연로딩


flush

영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것.

flush 발생 시:

flush 사용 법:

em.flush();        // 직접 호출
tx.commit();       // 트랜잭션 커밋 시 자동 호출
JPQL 쿼리 실행;     // JPQL 쿼리 실행 시 자동 호출

기본키 매핑

직접 할당

@Id
private Long id;

자동 생성(@GeneratedValue)

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

기본키 생성 전략 (strategy)

IDENTITY 전략

기본키 생성을 데이터베이스에 위임

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

SEQUENCE 전략

데이터베이스 시퀀스를 사용해 기본키 할당

@Entity
@SequenceGenerator(
    name = "cat_seq_generator",
    sequenceName = "cat_seq",
    initialValue = 1, 
    allocationSize = 50
)
public class Cat {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator = "cat_seq_generator")
    private Long id;
}

TABLE 전략

키 생성 전용 테이블을 만들어 시퀀스를 흉내내는 전략

AUTO 전략 (기본값)

DB 방언(Dialect) 에 따라 자동으로 선택

@Id
@GeneratedValue(strategy = GenerationType.AUTO) // 또는 생략 가능
private Long id;
···
← PREV [리팩토링] 레거시 코드와 SRP NEXT → [JPA] 프록시, 지연로딩, 영속성 전이