BackEnd/Spring

[Spring] JPA 정리

Wonol 2022. 7. 22. 10:49
반응형

개발을 배우면서 처음 Java에서 DB 와 관련된 작업을 할 때는 JDBC API를 배웠습니다. JDBC API는 항상 모든 로직 안에서 Connection 객체를 받아오고, SQL 문을 작성하고, 끝나면 close 시켜야 했습니다. 그리고 이러한 중복되고 불필요한 코드를 작성하는 것을 보완해주는 MyBatis, Spring JdbcTemplate 를 배워 사용하였습니다.

최근에는 ORM 기술을 통해 SQL 작성없이 객체를 DB에 직접 저장/관리를 할 수 있게 도와주는 JPA 를 사용하여 DB 작업을 대부분 사용하고 있습니다.

이번 글에서는 이 JPA에 대해서 정리를 해보고자 합니다.


0. ORM(Ojbect-Relational Mapping) 이란?

- JPA에 대해서 알기전에 기본이 되는 ORM에 대해서 알아야 합니다.

- ORM : 객체는 객체대로 설계하고, RDB(관계형 데이터베이스)는 RDB 대로 설계한다.

- 프레임워크로 객체와 RDB 를 중간에서 서로 매핑해주는 역할을 담당한다.

- 대중적인 언어에는 대부분 ORM 기술이 존재한다.

  • ORM vs SQL Mapper(Mybatis, Spring JdbcTemplate)
    • ORM
      - DB 테이블을 Java 객체로 Mapping(매핑)함으로써 객체 간의 관계를 바탕으로 SQL문을 자동으로 생성(객체를 통해 간접적으로 디비 데이터를 조작)
      - DB 데이터 ← Mapping → Object 필드
      - SQL 쿼리가 아니라 메서드로 데이터를 조작
      - 객체간 관계를 바탕으로 SQL 을 자동으로 생성
    • SQL Mapper
      - SQL 을 직접 명시(SQL 문으로 직접 디비를 조작)
      - SQL ← Mapping → Object 필드
    • ORM은 RDB의 관계를 Object 에 반영하는 것이 목적이라면, SQL Mapper 는 단순히 필드를 매핑시키는 것이 목적이라는 차이점이 있습니다.

- 과거 EJB 시절에 만들어놓은 ORM 개념이 있었으나, 사용하기도 까다롭고 성능상 좋지 않아 사용되지 않았습니다.

1. JPA(Java-Persistence API) 란?

- Java 진영에서의 ORM 기술 표준으로,  Java Application 에서 RDB를 사용하는 방식을 정의한 인터페이스의 모음이다.

- 즉, 실제로 동작하는 것이 아닌 인터페이스를 구현하여 사용해야 한다.(대표적으로 Hibernate)

- JPA 2.1 표준 명세를 구현한 3가지 구현체가 있다.

  • Hibernate
  • EclipseLink
  • DataNucleus

- SQL 작성없이 객체를 데이터베이스에 직접 저장할 수 있게 도와주는 기술로 Application 과 JDBC 사이에서 동작한다.

2. SQL을 직접 다룰 때의 문제점(ex: MyBatis)

  1. 반복적인 코드의 작성
    - 모든 DB 작업에 대한 SQL 문을 작성해야 한다.
    - 반복적인 CURD SQL 작성과 객체를 SQL 에 매핑하는 코드를 작성하는데 시간이 오래 걸린다.
  2. SQL 의존적 개발
    - 테이블에 Column 이 추가된다면 모든 DAO의 SQL 문 변경이 필요하다.
    - 만약 동작하지 않으면 Java 에서의 로직과 SQL 문 둘 다 확인이 필요하다.
  3. 패러다임의 불일치
    - Java 는 객체지향 언어이지만, RDB(관계형 데이터베이스)는 객체지향이 다루는 개념이 존재하지 않고 서로 지향하는 목적이 다르기 때문에 패러다임의 불일치가 발생한다.
    - 이러한 서로 다른 목적 때문에 개발자가 중간에서 문제를 해결하기 위해 직접 코드를 작성해서 매핑해야 한다.

3. JPA 장단점

- 장점

  1. 생산성
    - JPA 에 객체를 전달만 하면 되므로 SQL 을 작성하고 JDBC API 를 사용하는 반복적인 일을 JPA 가 대신 처리해주어 생산성이 향상된다.
    - DDL 도 JPA가 자동으로 생성해주기 때문에 DB 설계 중심을 객체 설계 중심으로 변경할 수 있다.
  2. 유지보수
    - SQL 을 직접 다룰 때는 필드를 하나를 추가/삭제하여도 관련된 SQL 과 JDBC 코드를 전부 수정해야 했지만, JPA 는 이를 대신 처리해주어 유지보수가 줄어든다.
  3. 패러다임의 불일치 해결
    - JPA는 연관된 객체를 사용하는 시점에 SQL을 전달할 수 있고, 같은 트랜잭션 내에서 조회할 때 동일성도 보장하기 때문에 다양한 패러다임의 불일치를 해결한다.
  4. 성능
    - 어플리케이션과 데이터베이스 사이에서 성능 최적화 기능을 제공한다.
    - 같은 트랜잭션 안에서는 같은 엔티티를 반환하기 때문에 통신 횟수를 줄일 수 있다.
  5. 데이터 접근 추상화와 벤더 독립성
    - RDB는 같은 기능이라도 벤더마다 사용법이 다르기 때문에 처음 선택한 DB에 종속되고 변경이 어렵다.
    - JPA는 어플리케이션과 데이터베이스 사이에서 추상화된 데이터 접근을 제공하여 종속되지 않도록 도와준다.

- 단점

  1. 학습 곡선이 높다.
    - JPA 를 사용하려면 객체와 관계형 데이터베이스를 어떻게 매핑해야 하는지 학습한 후에 JPA 의 핵심 개념들을 이해해야 한다.
    - JPA 의 핵심 개념인 영속성 컨텍스트에 대한 이해가 부족하면 SQL 을 직접 사용해서 개발하는 것보다 못한 상황이 발생할 수 있다.
  2. 속도 저하 가능성이 있다.
    - 프로젝트의 규모가 크고 복잡하여 설계가 잘못된 경우, 속도 저하 및 일관성을 무너뜨릴 수 있다.
    - 복잡하고 무거운 Query 는 속도를 위해 별도의 튜닝이 필요하기 때문에 결국 SQL 문을 써야 할 수 있다.

4. 동작 과정

- JPA 는 어플리케이션과 JDBC API 사이에서 동작한다.

- 개발자가 JPA 를 사용하면, JPA 내부에서는 JDBC API 를 사용하여 SQL 을 생성하여 DB와 통신한다.

  • JPA 에서의 CRUD

- 간단하게 JPA 에서의 CRUD 메소드는 아래와 같다.

jpaEm.persist(member);	  // 저장
jpaEm.find(memberId);     // 조회
jpaEm.remove(member);     // 삭제

// 수정(조회 이후 해당 객체에 변경하면 DB에도 변경된다.)
// 영속성 컨텍스트
Member member = jpaEm.find(memberId);	
member.setName("변경할 이름");

- JPA 에서는 수정 메소드를 제공하지 않는다.
- 하지만 수정은 필요한 동작이기 때문에 JPA 에서는 데이터 수정 시 , 매핑된 객체(테이블 데이터)를 조회해서 값을 변경 후 커밋하면 DB 서버에서 UPDATE 문으로 바꾸어 UPDATE 를 실행한다.

JPA에서의 수정은 Java 컬렉션에 수정하는 것처럼 컬렉션(ex: List)에서 조회해온 데이터를 .setName() 으로 수정한 다음 다시 컬렉션에 .add() 하지 않는 것처럼 생각하면 된다.

4-1. 저장(persist = insert)

- Member 를 저장할 때, JPA는 아래와 같이 동작한다.

  1. Member 객체를 JPA 에 넘긴다.
  2. JPA는 객체의 엔티티(Entity)를 분석한다.
  3. JPA에서 INSERT 쿼리(SQL)를 생성한다.
  4. JPA가 JDBC API 를 사용하여 생성한 SQL를 DB에 요청한다.

4-2. 조회(find = select)

- Member 를 조회할 때, JPA는 아래와 같이 동작한다.

  1. Member 객체 필드에서 PK(Primary Key) 값을 JPA 에 넘긴다.
  2. JPA에서 Member 엔티티(Entity)의 매핑 정보를 바탕으로 쿼리를 생성한다.
  3. JPA가 JDBC API 를 사용하여 생성한 SQL를 DB에 요청한다.
  4. JPA가 JDBC API 를 통해 DB로 부터 결과를 받아온다.
  5. JPA는 결과(ResultSet)를 객체(Member)의 Entity에 맞게 모두 매핑한다.

- 쿼리를 JPA가 만들어 주기 때문에 Object와 RDB 간의 패러다임 불일치를 해결할 수 있다.

5. JPA 어노테이션 종류

- JPA 는 어노테이션을 분석하여 어떤 객체가 어떤 테이블과 관계가 있는지를 알아낸다.

어노테이션 설명
@Entity 데이터베이스의 테이블과 일대일로 매칭되는 객체 단위이며, 클래스를 테이블과 매핑한다고 JPA 에게 알린다.
@Table @Entity 선언된 클래스에 매핑할 테이블정보(테이블이름)을 알려준다.
이 어노테이션이 생략되면 클래스이름을 테이블이름으로 매핑한다. 
@Column 데이터베이스의 테이블에 있는 컬럼에 필드(변수)를 매핑한다.
별다른 옵션을 설정하지 않는다면 기본적으로는 생략 가능하다.
@Id @Entity 선언된 클래스의 필드를 테이블의 기본키(Primary Key)에 매핑한다.
@GeneratedValue 새로운 레코드가 생성될 때마다 마지막 PK 값에서 자동으로 +1 을 해줘야 하는 auto increment 컬럼인 것을 알려준다.
@EmbeddedId 복합키로서 정의된 값을 정의하고자 할 때 사용한다.
@Enumerated Java 의 Enum 형태로 되어 있는 미리 정의되어 있는 코드 값이나 구분값을 데이터 타입으로 사용할 때 사용한다.
@Transient Entity 객체에 속성으로 지정되어 있지만, 데이터베이스 에서는 필요없는 속성일 때 사용한다.
해당 속성을 데이터베이스에서 이용하지 않겠다라는 정의이다.
Entity 객체에 임시로 값을 담는 용도로 사용한다.

6. 번외

- 아래 사진은 위의 내용을 요약하며 JPA, Hibernate, Spring Data JPA 의 전반적인 개념을 그림으로 표현한 것입니다.

- JPA와 Spring Data JPA는 똑같이 JPA가 들어가서 처음 접할 때 JPA라고 말하면 Spring Data JPA라고 생각할 수 있는데, 다른 개념이기 때문에 헷갈리면 안 된다.

- JPA는 ORM을 위한 자바 EE 표준이며, Spring Data JPA 는 JPA를 쉽게 사용하기 위해 스프링에서 제공하는 프레임워크이다.

- 추상화 정도는 Spring Data JPA > Hibernate > JPA 이다.

- Hibernate를 쓰는 것과 Spring Data JPA를 쓰는 것 사이에는 큰 차이가 없지만 아래와 같은 이유로 Spring Data JPA를 사용하는 것이 더 좋을 수 있다.

  • 구현체 교체의 용이성
  • 저장소 교체의 용이성

참고

- https://gmlwjd9405.github.io/2019/08/04/what-is-jpa.html

- https://suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/

- https://mkil.tistory.com/526

- https://velog.io/@adam2/JPA%EB%8A%94-%EB%8F%84%EB%8D%B0%EC%B2%B4-%EB%AD%98%EA%B9%8C-orm-%EC%98%81%EC%86%8D%EC%84%B1-hibernate-spring-data-jpa#%EA%B7%B8%EB%9F%BC-spring-data-jpa-%EA%B0%99%EC%9D%80%EA%B1%B4-%EB%AD%90%EC%A3%A0

반응형