JPA는 자바 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용되는 인터페이스의 모음이다.
그 말은 즉, 실제적으로 구현된것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크이다.
JPA를 구현한 대표적인 오픈소스로는 Hibernate가 있다.
# Persistence Framework란?
영속성(Persistence)은 프로그램이 종료되어도 데이터가 사라지지 않는 특성을 뜻한다.
[그림 1]에서 보이는 Persistence 계층이 Domain Model 계층에 영속성을 부여하는 역할을 한다.
자바에서는 JDBC를 통해 영속성을 부여할 수 있다.
하지만 JDBC는 중복된 코드를 반복적으로 사용하거나 Connection과 같은 공유 자원 관리가 까다로운 등 여러가지 단점이 있다.
이러한 JDBC 프로그래밍의 복잡함이나 번거로운 작업 없이 간단한 작업만으로 Persistence 계층을 구현할 수 있는 Persistence Framework들이 존재한다.
대표적으로 SQL Mapper 나 ORM 기술이 바로 Persistence Framwork의 일종이다.
# RDB와 객체지향 사이의 패러다임 불일치
- 상속의 개념
RDB에서는 객체지향에서의 상속의 개념이 없다.
- 연관관계
RDB에서는 연관관계가 있을 때 외래 키로 참조하지만, 자바에서는 객체참조를 통해서 이루어진다.
- N:M관계시 RDB에서는 조인 테이블이 필요
자바에서는 N:M 관계를 양쪽에 객체 참조를 통해 표현할 수 있지만, RDB에서는 새로운 테이블을 만들어야 한다.
- 동일성
RDB에서는 기본 키를 통해서 데이터를 구별하지만, 자바에서는 객체 식별과, equals 메소드를 사용해서 구별한다.
- 탐색 / 순회
RDB에서는 필요한 테이블들만 참조해서 원하는 정보를 가져가지만, 자바에서는 객체 참조만 있다면 계속해서 다른 객체들을 탐색/ 순회할 수 있다.
ORM (Object Relational Model)이란
OOP(객체 지향 프로그래밍)와 RDB(Relational DataBase)를 연결할 계층의 역할로 제시된 패러다임입니다.
RDB의 Model을 OOP의 Entity형태로 투영시키는 방식을 사용합니다.
객체 관계 매핑 객체와 관계형 데이터베이스의 불일치를 자동으로 매핑해주는 기술로 우리가 일반 적으로 알고 있는 애플리케이션 Class와 RDB 의 테이블을 매핑(연결)한다는 뜻이며, 기술적으로는 어플리케이션의 객체를 RDB 테이블에 자동으로 영속화 해주는 것이라고 보면됩니다.
시스템에 따라, 사용하는 DB 및 DB Connector에 따라 달라질 수 있는 데이터 매핑 구조를 객체지향 형태로 통일시켜, SQL 구조의 DB를 OOP구조의 형태로 매핑시키려는 패러다임입니다.
ORM에 대한 가장 핵심 키워드는 객체 모델과 관계형 모델 간의 불일치 입니다.
객체지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용하기 때문에 객체 모델과 관계형 모델 간에 불일치가 존재 합니다. 따라서 ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결 합니다.
즉, 객체를 통해 간접적으로 데이터 베이스 데이터를 다룬다 고 생각하시면 됩니다.
oop 관련내용 참고
장점
- SQL문이 아닌 Method를 통해 DB를 조작할 수 있어, 개발자는 객체 모델을 이용하여 비즈니스 로직을 구성하는데만 집중할 수 있습니다. (내부적으로는 쿼리를 생성하여 DB를 조작함. 하지만 개발자가 이를 신경 쓰지 않아도됨)
- Query와 같이 필요한 선언문, 할당 등의 부수적인 코드가 줄어들고 각종 객체에 대한 코드를 별도로 작성하여 코드의 가독성을 높임
- 객체지향적인 코드 작성이 가능하다. SQL의 절차적이고 순차적인 접근이 아닌 객체지향적인 접근으로 인해 오직 객체지향적 접근만 고려하면 되기때문에 생산성 증가
- 매핑하는 정보가 Class로 명시 되었기 때문에 ERD를 보는 의존도를 낮출 수 있고 재사용 및 유지보수, 리팩토링에 유리
- DBMS에 대한 종속성이 줄어듭니다 예를들어 기존 방식에서 MySQL 데이터베이스를 사용하다가 PostgreSQL로 변환한다고 가정해보면, 새로 쿼리를 짜야하는 경우가 생김. 이런 경우에 ORM을 사용한다면 쿼리를 수정할 필요가 없음
- 잘쓴다면 성능에 도움이 됩니다
단점
- 프로젝트의 규모가 크고 복잡하여 설계가 잘못된 경우, 속도 저하 및 일관성을 무너뜨리는 문제점이 생길 수 있음
- → ORM이 자동으로 쿼리를 날려주기 때문에 ORM이 어떤 쿼리를 날리는지 신경써야함
- 복잡하고 무거운 Query는 속도를 위해 별도의 튜닝이 필요하기 때문에 결국 SQL문을 써야할 수도 있음하위 타입 문제(RDB에는 상속의 개념이 없기 때문)
- 하위 타입문제 (RDB 에는 상속의 개념이 없음)
- 동일성 문제 (Java의 경우 Equals로 비교하지만 RDB는 PK를 기준으로 함)
- 연관 관계(연관의 방향성을 지정해주어야 함. RDB는 방향성이 없음.)
- DBMS의 고유 기능을 이용하기 어렵다. (하지만 이건 단점으로만 볼 수 없다 : 특정 DBMS의 고유기능을 이용하면 이식성이 저하된다.)
- 학습비용이 비쌈
JPA(Java Persistence API) 이란
- Java 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용하는 인터페이스 모음
- 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
- 인터페이스 이기 때문에 Hibernate, OpenJPA 등이 JPA를 구현함
# 왜 JPA를 사용해야 할까?
JPA는 반복적인 CRUD SQL을 처리해준다. JPA는 매핑된 관계를 이용해서 SQL을 생성하고 실행하는데, 개발자는 어떤 SQL이 실행될지 생각만하면 되고, 예측도 쉽게 할 수 있다. 추가적으로 JPA는 네이티브 SQL이란 기능을 제공해주는데 관계 매핑이 어렵거나 성능에 대한 이슈가 우려되는 경우 SQL을 직접 작성하여 사용할 수 있다.
JPA를 사용하여 얻을 수 있는 가장 큰 것은 SQL아닌 객체 중심으로 개발할 수 있다는 것이다. 이에 따라 당연히 생산성이 좋아지고 유지보수도 수월하다. 또한 JPA는 패러다임의 불일치도해결하였다. 예를 들면 JAVA에서는 부모클래스와 자식클래스의 관계 즉, 상속관계가 존재하는데 데이터베이스에서는 이러한 객체의 상속관계를 지원하지 않는다(상속 기능을 지원하는 DB도 있지만 객체 상속과는 다름). 이런 상속관계를 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 객체는 어떻게 저장할까? 위에서 봤던 상속구조와 다를바가 없다. 아래의 JAVA 코드를 살펴보도록 하자.
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는 아래와 같은 코드를데이터베이스에게 실행하라고 할것이다.
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는 아니다.
# 하이버네이트
1차 캐시 : 영속성 컨텍스트라는 저장소를 트랜잭션과 1:1로 연결하여, 저장소에 객체에 변화를 저장하고, 트랜잭션이 끝날 때 이를 DB에 반영하는 기술
→ 쓸때없는 쿼리가 줄어듬
dirty checking : 한 트랜잭션 내에서 하이버네이트가 관리하는 객체가 변경되었는지 감지해서 DB에 반영
write-behind : 하이버네이트가 관리하는 객체의 상태 변화를 가능한 늦게 DB에 반영하는 것
drity checking과 write-behind를 통해서 하이버네이트는 특정 컬럼만 update 하는 것이 가능하다.
연관된 글 :
[Spring] JDBC(Java Database Connectivity)란? JDBC 드라이버란?
참고 :
'개발 > Spring' 카테고리의 다른 글
[Spring] h2 DB 연결하고 JPA 사용하기 (0) | 2023.04.27 |
---|---|
[Spring] 스프링시큐리티(Spring Security) 개념 (0) | 2023.04.26 |
[Spring] spring 기초 (0) | 2023.03.09 |
[Spring] 어노테이션 모음집 (0) | 2023.03.08 |
[Spring] QueryDSL (0) | 2023.02.20 |