1. 소개
JPA는 자바에서 ORM(Object-Relational Mapping) 기술 표준으로 사용되는 인터페이스의 모음이다.
실제적으로 구현된 것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크이다.
JPA를 사용하면 SQL을 직접 작성하지 않고도 엔티티(Entity) 객체를 이용해 데이터베이스와 상호작용할 수 있다.
JPA를 구현한 대표적인 오픈소스로는 Hibernate가 있다.
ORM( Object-Relational Mapping) 이란?
우리가 일반적으로 알고 있는 애플리케이션 class와 RDB(Relational DataBase)의 테이블을 매핑(연결)한다는 뜻이며, 기술적으로는 애플리케이션의 객체를 RDB 테이블에 자동으로 영속화해주는 것이라고 보면 된다.
장점
- SQL문이 아닌 Method를 통해 DB를 조작할 수 있다, 개발자는 객체 모델을 이용하여 비즈니스 로직을 구성하는데만 집중할 수 있음.
- 내부적으로 쿼리를 생성하여 DB를 조작하지만 개발자가 신경 쓰지 않아도 된다.
- Query와 같이 필요한 선언문, 할당 등의 부수적인 코드가 줄어들어, 각종 객체에 대한 코드를 별도로 작성하여 코드의 가독성을 높임
- 객체지향적인 코드 작성이 가능하다. 오직 객체지향적 접근만 고려하면 되기 때문에 생산성 증가
- 매핑하는 정보가 Class로 명시되어 있기 때문에 ERD를 보는 의존도를 낮출 수 있고 유지보수 및 리팩토링에 유리
- 예를 들어 기존 방식에서 MySQL 데이터베이스를 사용하다가 PostgreSQL로 변환한다고 생각해 보면, 새로 쿼리를 짜야한다. 이런 경우에 ORM을 사용한다면 쿼리를 수정할 필요가 없음
단점
- 프로젝트의 규모가 크고 복잡하여 설계가 잘못된 경우, 속도 저하 및 일관성을 무너뜨리는 문제점이 생길 수 있음
- 복잡하고 무거운 Query는 속도를 위해 별도의 튜닝이 필요하기 때문에 결국 SQL문을 써야 할 수도 있음
- 학습비용이 비쌈
JPA(Java Persistence API) 란?
- Java 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용하는 인터페이스 모음
- 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
- Hibernate, OpenJPA 등이 JPA를 실제로 구현한 것들
주요 개념
엔티티 (Entity)
JPA에서 데이터베이스의 테이블과 매핑되는 자바 클래스
@Entity 어노테이션을 사용하여 선언한다.
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String name;
private String email;
// 기본 생성자 및 getter, setter 필요
}
- @Id : PK 지정
- @GeneratedValue: AUTO_INCREMENT
영속성 콘텍스트 (Persistence Context)
JPA에서 엔티티 객체를 관리하는 메모리상의 공간으로, 데이터베이스와 애플리케이션 사이에서 변경 사항을 추적하고 자동으로 동기화하는 역할을 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myJpaUnit");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin(); // 트랜잭션 시작
User user = new User();
user.setName("Alice");
user.setEmail("alice@example.com");
em.persist(user); // 영속성 컨텍스트에 저장
em.getTransaction().commit(); // 트랜잭션 종료 (DB 반영)
저장
em.persist(newUser);
조회
User user = em.find(User.class, 1L);
수정
user.setName("Updated Name"); // 영속 상태에서는 자동 반영
삭제
em.remove(user);
주요 어노테이션
어노테이션 | 설명 |
@Entity | 엔티티 클래스 지정 |
@Table(name="table_name") | 테이블 이름 지정 (생략 시 클래스명 사용) |
@Id | Primary key 지정 |
@GeneratedValue(strategy = GenerationType.IDENTITY) | 자동 증가 (Auto Increment) 설정 |
@Column(name="column_name", nullable=false) | 테이블 컬럼 지정 |
@OneToMany, @ManyToOne, @OneToOne, @ManyToMany | 관계 매핑 |
Why JPA?
JPA는 반복적인 CRUD SQL을 처리해 준다.
JPA는 매핑된 관계를 이용해서 SQL을 생성하고 실행하는데, 개발자는 어떤 SQL이 실행될지 생각만 하면 되고, 예측도 쉽게 할 수 있다. 추가적으로 JPA는 네이티브 SQL이란 기능을 제공해 주는데 관계 매핑이 어렵거나 성능에 대한 이슈가 우려되는 경우 SQL을 직접 작성하여 사용할 수 있다.
JPA를 사용하여 얻을 수 있는 가장 큰 장점은 SQL이 아닌 객체 중심으로 개발할 수 있다는 것이다. 이에 따라 당연히 생산성이 좋아지고 유지보수도 수월한다. 또한 JPA는 패러다임의 불일치도 해결하였다. 예를 들면 JAVA에서는 부모클래스와 자식클래스의 관계 즉, 상속관계가 존재하는데 데이터베이스에서는 이러한 객체의 상속관계를 지원하지 않는다. 이런 상속관계를 JPA는 아래와 같은 방식으로 해결하였다.
동작 방식
위 구조에서 Album 클래스를 저장한다고 가정할 때
// Album 객체저장
jpa.persist(album);
그러면 JPA는 위의 코드를 아래의 쿼리로 변환해서 실행한다.
INSERT INTO ITEM (ID, NAME, PRICE)
INSERT INTO ALBUM (ARTIST)
위처럼 저장하면 당연히 조회할 때도 두 테이블을 엮어서 가져올 것이다.
조회하는 Java코드와 변환되는 쿼리를 보면 알 수 있다.
// JAVA 코드
String albumId = "id100";
Album album = jpa.find(Album.class, albumId);
// 변환된 쿼리
SELECT I.*, A.* FROM ITEM I JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID
위와 같이 상속관계에 대한 접근도 제공해 주는데 객체지향에는 연관관계라는 것도 있다.
코드로 말하자면 class에서 또 다른 class type을 필드 변수로 가지고 있는 것이다.
객체관계와 이를 테이블 구조로 나타낸 것이 아래의 그림이다.
위의 그림은 Member 클래스가 Team 타입의 team 필드 변수를 가지고 있는 형태인데, 코드로 나타내면 아래와 같다.
class Member {
String id;
Team team;
String username;
}
class Team {
Long id;
String name;
}
그렇다면 Team 객체를 참조하는 필드를 가지고 있는 Member 객체는 어떻게 저장할까?
위에서 봤던 상속구조와 똑같다.
Member member = new Member();
member.setId("100");
member.setUsername("dbjh");
Team team = new Team();
team.setName("dev_team");
member.setTeam(team);
jpa.persist(member);
위처럼 Member 객체의 team 필드에 Team 객체를 set 하고 Member 객체를 DB에 저장하게 되면 JPA는 아래와 같은 코드를 DB에게 실행하라고 할 것이다.
INSERT INTO MEMBER (ID, TEAM_ID, USERNAME)
INSERT INTO TEAM (ID, NAME)
이렇게 저장 후 Member 객체만 조회하면, Team 객체 정보도 가져와서 Member 객체의 team 필드에 주입해 주기 때문에 아래와 같이 사용할 수 있다.
// JAVA 코드
Member member = jpa.find(Member.class, memberId); Team team = member.getTeam();
// 변환된 쿼리
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T
ON M.TEAM_ID = T.TEAM_ID
위와 같은 구조들이 더 복잡해져도 JPA는 모두 지원해 주기 때문에 문제없이 사용할 수 있다.
JPA의 저장 및 조회는 아래와 같은 구조로 실행된다.
저장
조회
또한 JPA는 수정 메서드를 제공하지 않는다. 하지만 당연히 수정은 필요하기 때문에 JPA는 데이터 수정 시, 매핑된 객체를 조회해서 값을 변경 후 커밋하면 DB 서버에 UPDATE 문을 전송하여 UPDATE를 실행한다.
추가적으로 알아둬야 할 것은, 스프링에서 흔히 사용하는 것으로 알고 있는 JPA는, JPA를 이용하는 spring-data-jpa 프레임워크이지 JPA는 아니다.
정리
비교항목 | JPA | JDBC (SQL 직접 사용) |
개발 생산성 | SQL 직접 작성할 필요 X | SQL 작성 및 매핑 코드가 많음 |
유지보수 | 엔티티 중심 개발로 유지보수 용이 | SQL이 많아 변경 시 어려움 |
성능 최적화 | 1차 캐시, 지연 로딩, 트랜잭션 관리 기능 제공 | 개발자가 직접 최적화해야 함 |
객체 중심 개발 가능
데이터베이스 변경 시에도 코드 수정 최소화 가능
자동으로 SQL 생성해 줘서 생산성 향상
영속성 콘텍스트를 활용한 성능 최적화 가능
데이터베이스 작업을 효율적으로 처리하기 위한 도구!
참조
'Spring' 카테고리의 다른 글
[Spring] 동적으로 HTTP 상태 코드 설정 (0) | 2025.02.04 |
---|---|
[Spring] Bean (0) | 2025.02.04 |
[Spring] IoC, DI, AOP (0) | 2025.02.04 |
[Spring] 컴포넌트 스캔(Component Scan) 정리 (0) | 2025.02.04 |
[Spring] 스프링 부트(Spring Boot)란? (0) | 2025.02.04 |