얼마 전 Spring 정리를 하며 동시에 Spring 강의를 듣고 현재 개발을 진행 중인 작성자로써 JPA라는 것이 어떤 일을 하는 지 어느 정도 알고 있다. 하지만 이것이 정확하게 어떤 것이고 어디서 어떤 방식으로 사용 가능한지 명확하게 조사해 본 적이 없어 이 글을 쓴다.
우선 항상 그렇듯 위키백과의 내용을 가져와 본다.
자바 퍼시스턴스(Java Persistence, 이전 이름: 자바 퍼시스턴스 API/Java Persistence API) 또는 자바 지속성 API(Java Persistence API, JPA)는 자바 플랫폼 SE와 자바 플랫폼 EE를 사용하는 응용프로그램에서 관계형 데이터베이스의 관리를 표현하는 자바 API이다.
기존에 EJB에서 제공되던 엔터티 빈(Entity Bean)을 대체하는 기술이다. 자바 퍼시스턴스 API는 JSR 220에서 정의된 EJB 3.0 스펙의 일부로 정의가 되어 있지만 EJB 컨테이너에 의존하지 않으며 EJB, 웹 모듈 및 Java SE 클라이언트에서 모두 사용이 가능하다. 또한, 사용자가 원하는 퍼시스턴스 프로바이더 구현체를 선택해서 사용할 수 있다.
위키백과를 보니 지난 ORM 게시물에서 봤던 단어인 Persistence가 보인다. Persistence, 즉 영속성의 개념을 다시 가져와 보자.
이는 곧 Object Persistence(영속성 객체)를 만들어 RDB와 api로 정보를 주고 받기 위해 필요한 내용들을 Java에서 사용할 수 있도록 Java로 만든 API이다.
여기서 JPA는 자바플랫폼 SE&EE를 사용하는 Program에서 RDB를 관리할 때 필요한 Java API이다.
이 부분을 요약하자면 아래와 같다.

사실 장점과 단점은 결국 JPA도 ORM이기 때문에 장단점을 공유한다. ORM의 장단점에 더해 JAP의 장단점도 추가해서 정리해봤다.
생산성
JPA에 객체를 전달만 하면 동작하며 SQL 작성, JDBC API를 사용하는 반복적 과정을 JPA가 대신 처리해줘서 생산성이 올라간다.
DDL도 JPA가 자동 생성해주기 때문에 DB 설계 중심을 객체 설계 중심으로 변경 가능하다.
유지보수
SQL을 직접 다룰 땐 필드 하나만 수정되어도 관련된 SQL과 JDBC 코드를 전부 수정해야 했지만 JPA는 이를 대신 해주기 때문에 코드 변경 사항이줄어 유지보수 사항이 줄어든다.
패러다임의 불일치 해결
JPA는 연관 객체를 사용하는 시점에 SQL을 전달 할 수 있고 같은 트랜잭션 내 조회 시 동일성도 보장하기 때문에 다양한 패러다임 불일치의 문제를 해결 할 수 있다.
성능
어플리케이션과 DB 사이의 성능 최적화를 제공한다.
같은 트랜잭션 안에선 같은 엔티티를 반환하기 때문에 통신 횟수를 줄일 수 있다.
데이터 접근 추상화와 벤더 독립성
RDB는 같은 기능이라도 벤더마다 사용법이 달라 선택한 DB에 종속되기 마련이다.
JPA는 어플리케이션과 데이터베이스 사이에 들어가는 추상화된 객체로 동작하면서 데이터 접근을 제공하기 때문에 종속되지 않도록 도와준다.
학습 곡선이 높다.
JPA 를 사용하려면 객체와 관계형 데이터베이스를 어떻게 매핑해야 하는지 학습한 후에 JPA 의 핵심 개념들을 이해해야 한다.
JPA 의 핵심 개념인 영속성 컨텍스트에 대한 이해가 부족하면 SQL 을 직접 사용해서 개발하는 것보다 못한 상황이 발생할 수 있다.
속도 저하 가능성이 있다.
프로젝트의 규모가 크고 복잡하여 설계가 잘못된 경우, 속도 저하 및 일관성을 무너뜨릴 수 있다.
복잡하고 무거운 Query 는 속도를 위해 별도의 튜닝이 필요하기 때문에 결국 SQL 문을 써야 할 수 있다.
스프링에서 JPA를 사용해 본 만큼 스프링에서 JPA를 사용하는 과정을 소개한다.
필자가 사용했던 DB는 H2였기에 여기서도 H2를 바탕으로 설명한다.
H2 설치
~/test 이곳에 생성
~/test.mv.db 파일 생성 확인
jdbc:h2:tcp://localhost/~/test 형식으로 접속
테이블 생성
여기서는 아래와 같이 테이블을 생성했다. 해당 테이블 생성은 h2를 실행(위의 접속 주소) 해서 직접 테이블을 생성했다.
drop table if exists member CASCADE;
create table member
(
id bigint generated by default as identity,
name varchar(255),
primary key (id)
);
H2와 JPA 관련 라이브러리를 build.gradle의 dependencies에 추가
H2와 JDBC의 라이브러리를 잡아준다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
application.properties에 설정 추가.
스프링 부트를 실행할 때 사용하는 properties에 H2를 실행할 때 필요한 위치, 정보, ID와 PW 등을 기입한다.
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
엔티티 매핑
JPA로 관리해야 할 클래스에 @Entity 어노테이션을 달고 그 안에 Primary Key로 사용되는 id에 @Id 어노테이션을 기입한다.
package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
JPA에서 관리되는 객체를 지정하고 컨트롤 할 때 사용하는 어노테이션의 정보는 아래와 같다.
| 어노테이션 | 설명 |
|---|---|
| @Entity | 데이터베이스의 테이블과 일대일로 매칭되는 객체 단위이며, 클래스를 테이블과 매핑한다고 JPA 에게 알린다. |
| @Table | @Entity 선언된 클래스에 매핑할 테이블정보(테이블이름)을 알려준다. 이 어노테이션이 생략되면 클래스이름을 테이블이름으로 매핑한다. |
| @Column | 데이터베이스의 테이블에 있는 컬럼에 필드(변수)를 매핑한다. 별다른 옵션을 설정하지 않는다면 기본적으로는 생략 가능하다. |
| @Id | @Entity 선언된 클래스의 필드를 테이블의 기본키(Primary Key)에 매핑한다. |
| @GeneratedValue | 새로운 레코드가 생성될 때마다 마지막 PK 값에서 자동으로 +1 을 해줘야 하는 auto increment 컬럼인 것을 알려준다. |
| @EmbeddedId | 복합키로서 정의된 값을 정의하고자 할 때 사용한다. |
| @Enumerated | Java 의 Enum 형태로 되어 있는 미리 정의되어 있는 코드 값이나 구분값을 데이터 타입으로 사용할 때 사용한다. |
| @Transient | Entity 객체에 속성으로 지정되어 있지만, 데이터베이스 에서는 필요없는 속성일 때 사용한다. |
위의 코드에선 @Entity와 @Id, @GeneratedValue만 사용했지만 상세 정보를 설정하고 싶으면 위의 어노테이션을 참고해서 사용하면 된다.
JPA를 사용하는 Repository 생성
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface SpringDataJpaMemberRepository extends JpaRepository<Member,
Long>, MemberRepository {
Optional<Member> findByName(String name);
}
마지막으로 Repository를 구성한다.
앞서 말했듯이 JPA는 기본적으로 구성되고 어노테이션으로 잡아둔 정보는 자동으로 JDBC로 관리해주지만 위의 코드에서 보이는 name은 잡혀있지 않다. 따라서 Repository에 name을 JDBC로 관리 할 수 있도록 따로 함수를 잡아 주어야 관리가 가능하다.
JPA는 Java 환경에서 사용하는 ORM이다.
스프링에서는 아래의 과정을 따라가면 된다.