Java Persistence API
객체와 관계형 데이터베이스 사이의 매핑을 관리하는 표준 ORM 기술이다.
SQL 없이 객체지향 방식으로 DB 데이터를 처리할 수 있다.
@Entity 사용)JPA는 인터페이스고, 이를 구현한 대표적인 구현체로 Hibernate, EclipseLink, TopLink 등이 있다.
JPA 인터페이스를 대표적인 구현한 오픈소스는 HIbernate이며, 실질적인 기능은 Hibernate에 구현되어 있다.
| 항목 | 설명 |
|---|---|
| JPA | 표준 인터페이스 (기능 정의) |
| Hibernate | JPA를 구현한 대표적인 라이브러리 |
| Spring Data JPA | JPA를 더 쉽게 쓰기 위한 Spring의 추상화 라이브러리 (자동 Repository 생성 등) |
| 장점 | 설명 |
|---|---|
| 생산성 향상 | SQL을 직접 작성하지 않아도 되며, 코드 작성량이 줄어든다 |
| 유지보수 용이 | 객체 중심의 프로그래밍으로 로직 이해 및 변경이 쉬움 |
| DB 독립성 | DBMS 교체 시 SQL을 최소한만 수정 |
| 캐싱 및 지연 로딩 | 성능 향상을 위한 다양한 기능 제공 |
| 트랜잭션 관리 | Spring과 함께 쓰면 @Transactional 같은 선언형 트랜잭션 처리 가능 |
| 항목 | 설명 |
|---|---|
| 복잡한 쿼리 처리 | 통계 처리 같은 복잡한 쿼리의 경우 JPQL보다는 직접 SQL을 사용하는 것이 나을 수 있음 |
| 성능 저하 위험 | 객체 간 매핑 설계가 잘못되었거나, JPA가 자동 생성한 쿼리가 의도하지 않은 방식일 경우 성능 저하가 발생할 수 있음 |
| 학습 시간 | 제대로 사용하려면 알아야 할 부분이 많아 학습 시간이 오래 걸림 |
| 항목 | MyBatis | JPA |
|---|---|---|
| SQL 작성 | 수동 (직접 작성) | 자동 (필요시 JPQL) |
| 쿼리 제어력 | 매우 뛰어남 | 추상화되어 있음 |
| 학습 난이도 | 낮음 | 중간 이상 |
| 생산성 | 반복 코드 많음 | CRUD 자동 처리 |
| 데이터 접근 방식 | SQL 중심 | 객체 중심 |
| 유형 | Mapper 기반 | Entity 기반 |
| 대표 활용 예 | 복잡 쿼리, 성능 제어 | 도메인 중심 설계, 단순 CRUD |
데이터베이스 테이블과 매핑되는 자바 클래스
@Entity 어노테이션을 붙이면 JPA가 간리하는 영속 객체(Persistent Object)가 된다.
User, Post, Order 같은 도메인 클래스
EntityManager를 생성하는 팩토리 객체
애플리케이션이 시작될 때 단 1개만 생성된다. (싱글톤)
JPA의 시작점 역할을 하며, DB 연결 설정 등도 이곳에서 초기화한다.
실제로 데이터베이스와 CRUD 작업을 수행하는 객체
persist(), find(), remove() 등의 메소드를 제공한다
Hibernate를 사용하는 경우 내부적으로 자동 관리된다.
생성하는거 역시 개발자가 직접 생성하지 않고, Spring Data JPA가 자동으로 처리한다.
EntityManager가 관리하는 1차 캐시 공간
DB에서 조회된 객체를 메모리에 저장해서, 같은 ID의 엔티티는 하나만 존재하게 한다. (동일성 보장)
변경 사항을 감지해서 flush() 시점에 SQL로 반영한다.
JpaRepository등은 내부적으로EntityManager를 사용하여 작업을 수행
개발자가 직접 EntityManager를 사용할 필요 없이, 자동으로 영속성 컨텍스트와 1차 캐시가 적용된다.
Spring Data JPA는 JPA를 쉽게 쓰기 위한 껍데기(추상화) 역할을 한다.
SQL과 유사하지만 테이블이 아닌 Entity와 필드 기준으로 작성하는 쿼리
SELECT u FROM User u WHERE u.name = :name
트랜잭션을 관리하는 객체
Java SE 환경에서는 직접 begin/commit 해야한다.
Spring 에서는 @Transactional로 생략 가능하다. (자동 처리)
EntityManagerFactory -> EntityManager 생성
EntityManager는 내부적으로 Persistence Context(1차 캐시)를 관리
DB 작업은 전부 EntityManager가 처리
JpaRepository는 그 과정을 자동으로 대신 수행해 준다.
Spring Data JPA에서 제공하는 인터페이스로 JPA를 더 쉽게 사용할 수 있도록 도와주는 추상화 계층
별도 구현 없이 자동으로 CRUD 메소드 제공
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByName(String name);
List<Member> findByAgeGreaterThan(int age);
| 인터페이스 | 설명 |
|---|---|
Repository<T, ID> | 최상위 인터페이스 |
CrudRepository<T, ID> | 기본적인 CRUD 메서드 제공 (save(), findById() 등) |
PagingAndSortingRepository<T, ID> | 페이징과 정렬 기능 추가 |
JpaRepository<T, ID> | CrudRepository + 페이징 + JPA 특화 기능 포함 (가장 많이 사용됨) |
JpaRepository<T, ID>는 CrudRepository, PagingAndSortingRepository를 확장하고 있으며,
기본 CRUD 기능 + 페이징 + 정렬 기능을 모두 제공
| 범주 | 메서드 | 설명 |
|---|---|---|
| 저장/수정 | save(entity) | 엔티티 저장 또는 수정 |
saveAll(entities) | 여러 개 저장 또는 수정 | |
| 조회 | findById(id) | ID 기준 단건 조회 (Optional 반환) |
findAll() | 전체 목록 조회 | |
findAllById(ids) | ID 리스트로 여러 건 조회 | |
existsById(id) | 해당 ID가 존재하는지 여부 확인 | |
count() | 전체 레코드 수 반환 | |
| 삭제 | delete(entity) | 엔티티 삭제 |
deleteById(id) | ID 기준 삭제 | |
deleteAll() | 전체 삭제 | |
| 정렬 | findAll(Sort sort) | 정렬 조건에 따른 전체 조회 |
| 페이징 | findAll(Pageable pageable) | 페이징 + 정렬 포함 조회 (Page<T> 반환) |
Page<Member> members = memberRepository.findAll(
// PageRequest.of(페이지 번호, 페이지 크기, 정렬 기준)
PageRequest.of(
0, // 0번째 페이지 (첫 페이지)
10, // 한 페이지에 10개 항목
Sort.by("name") // name 속성을 기준으로 오름차순 정렬
)
);
| 어노테이션 | 설명 |
|---|---|
@Entity | 이 클래스가 JPA에서 관리되는 엔티티임을 선언. 테이블과 매핑됨. 필수로 기본 생성자 필요. |
@Table(name = "테이블명") | 매핑할 실제 테이블 이름 지정. 생략 시 클래스명이 테이블명으로 사용됨. |
@Id | 기본 키(primary key)로 사용할 필드에 지정. 필수 어노테이션. |
@SequenceGenerator, @TableGenerator | SEQUENCE, TABLE 전략 사용 시 생성기 정의. |
@Column(name = "컬럼명", nullable = false, length = 100) | 해당 필드를 특정 컬럼과 매핑. 길이, null 허용 여부 등 지정 가능. |
@Lob | BLOB/CLOB 타입으로 매핑 (대용량 문자/바이너리 저장). |
@Temporal(TemporalType.DATE) | Date, Calendar 타입 매핑 시 사용. DATE, TIME, TIMESTAMP 중 선택. |
@Transient | DB에 매핑하지 않음. JPA가 관리하지 않는 필드. |
| 어노테이션 | 설명 |
|---|---|
@ManyToOne | 다대일(N:1) 관계 설정. 보통 @JoinColumn과 함께 사용하여 외래 키 컬럼 지정. |
@OneToMany(mappedBy = "...") | 일대다(1:N) 관계 설정. 외래키는 상대 테이블에 존재. |
@OneToOne | 일대일(1:1) 관계 설정. |
@ManyToMany | 다대다(N:N) 관계 설정. 중간 테이블이 필요함. |
@JoinColumn(name = "외래키컬럼") | 연관된 외래 키 컬럼 지정. |
@JoinTable | 다대다 관계에서 중간 조인 테이블 정의 시 사용. |
@Embeddable | 다른 엔티티에 내장될 수 있는 객체 정의. |
@NamedQuery, @NamedQueries | 정적 쿼리 정의 (JPQL). |
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
// Getter/Setter 생략
}
// EntityManager를 이용한 저장
User user = new User();
user.setName("홍길동");
EntityManager em = ...;
em.getTransaction().begin();
em.persist(user); // INSERT 쿼리 실행
em.getTransaction().commit();
오늘은 JPA에 대해 공부했다.
JPA는 추상화된 데이터 접근 계층을 제공하기 때문에, 설정 파일만 변경하면 다양한 데이터베이스로 손쉽게 전환할 수 있어 시스템의 이식성과 유지보수성을 크게 높여준다. 또한 객체지향적인 프로그래밍이 가능해 SQL 중심의 개발 방식보다 더 직관적으로 비즈니스 로직에 집중할 수 있도록 도와준다.
데이터베이스에 새로운 컬럼이 추가되더라도 해당 클래스를 수정하는 것만으로도 쉽게 반영할 수 있어 반복적인 SQL 작성 없이 자동으로 매핑과 쿼리 처리가 이루어져 코드의 유지보수가 훨씬 간편해져 생산성 측면에도 장점이 있다.