[JPA] JPA란 ? 그리고 Spring Data JPA

JPA(Java Persistent API)란 ?

ORM (Object Relational Mapping) 객체 관계 매핑 기술의 표준으로 인터페이스의 모음입니다.

따라서 실제로 동작하는 것이 아니기 때문에 구현체가 필요한데, JPA 표준을 구현한 구현체는 아래와 같이 Hibernate, EclipseLink, DataNucleus가 있으며

대표적으로 Hibernate를 사용합니다.

 

ORM 이란 ?

ORM 기술은 말 그대로 객체와 관계형 데이터 베이스를 매핑해 주는 기술으로
객체는 객체대로 설계할 수 있고 관계형 데이터베이스는 관계형 데이터베이스대로 설계가 가능하도록 ORM 프레임워크가 중간에서 매핑을 해줍니다.

 

JPA의 동작 과정

JPA는 JAVA 애플리케이션과 JDBC 사이에서 동작하는데

JAVA 애플리케이션에서 JPA를 사용하면 내부에서 JDBC API를 사용해 SQL를 DB에 전달하고 결과를 반환받습니다.


객체 매핑

JPA에서 엔티티란, DB 테이블에 대응하는 하나의 클래스입니다.

@Entity 어노테이션이 붙은 클래스JPA가 각 필드의 어노테이션을 보고 DB테이블과 매핑해 관리 해주게 됩니다.

@Entity
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id;

    // 필드 이름이 카멜 케이스 일 경우 테이블 컬럼 이름을 언더 스코어로 자동 변환 해준다.
    // 따라서 아래 필드이름은 itemName으로 @Column을 사용 시 name 옵션을 사용하지 않아도 자동으로 item_name으로 변환 해준다.
    @Column(name = "item_name")
    private String itemName;

    // 컬럼명을 생략하면 필드명을 컬럼명으로 사용
    private Integer price;
    private Integer quantity;

    public Item() {} // JPA는 public, protected의 기본 생성자가 필수 !

    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}

 

매핑 어노테이션 설명

매핑 관련 어노테이션을 간단하게 알아보면 다음과 같습니다.

어노테이션 설 명
@Entity 테이블과의 매핑
@Entity가 붙은 클래스는 JPA가 관리하며
엔티티 이름을 지정하지 않으면 기본값인 클래스 이름을 사용합니다.
@Id 기본 키 매핑
@GeneratedValue 기본 키 생성 전략
GenerationType.IDENTITY 옵션은 기본 키 생성을 DB에 위임합니다.
@Column 객체 필드를 테이블 컬럼에 매핑
nullable 옵션 : null 값의 허용 여부 설정 (기본값 : true)
unique 옵션 : 유니크 제약조건
length 옵션 : 문자 길이 제약조건, String 타입에만 사용 (기본값 : 255)

 

@Entity 사용 시 주의 사항
  1. 접근 제어자가 public 또는 protected 인 기본 생성자가 필수
  2. 저장하려는 속성은 final이어서는 안된다.

JPA 사용 이유

생산성

JPA를 사용하면 자바 컬렉션에 저장하듯이 JPA에게 저장할 객체를 전달할 수 있습니다.

JPA가 간단한 CRUD를 알아서 다 짜주기 때문에 SQL을 작성하는데 많은 시간을 투자할 필요 없을 뿐더러, 반복적인 SQL을 만드는단순 반복 작업을 하지 않을 수 있게 됩니다.

 

유지보수

기존에는 필드 변경 시 모든 SQL을 수정해야 했지만

JPA를 사용하면 우리는 필드만 추가하면 SQL은 JPA가 알아서 자동으로 처리해줍니다.

 

패러다임 불일치 해결

개발자는 객체지향적으로 프로그래밍을 하고, JPA가 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성해 실행합니다.

JPA를 이용함으로써 개발자는 항상 객체 지향적으로 코드를 표현할 수 있게되어 더는 SQL에 종속적인 개발을 하지 않아도 됩니다.

JPA를 통해 객체 중심으로 개발을 할 수 있게 되다보니 생산성 향상은 물론 유지 보수또한 정말 편리해집니다.

 

성능

JPA는 다음과 같은 성능 최적화 기능들을 제공합니다.

 

- 1차 캐시와 동일성 보장

JPA는 같은 트랜잭션안에서는 같은 엔티티를 반환하기 때문에 DB와의 통신 횟수를 줄일 수 있습니다.

 

- 트랜잭션을 지원하는 쓰기 지원

트랜잭션을 commit 하기 전까지 INSERT SQL을 메모리에 쌓고 한번에 DB로 SQL을 전송합니다.

 

- 지연 로딩, 즉시 로딩

지연 로딩 : 객체가 실제 사용될 때 로딩
즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회 ( EX: 두 객체가 항상 JOIN이 필요한 경우 )

개발 초기에는 지연 로딩 설정으로 개발하다가, 만약 최적화가 필요하다면 즉시 로딩으로 변경할 수 있습니다.

 

데이터 접근 추상화와 벤더 독립성

각 DB 마다 사용법이 다르기 때문에 기존에 선택한 DB에서 다른 DB로 변경이 필요할 때, 이미 기존에 선택한 DB에 종속적이기 때문에 변경이 어렵습니다.

하지만 JPA는 인터페이스로 추상화된 데이터 접근을 제공하기 때문에 DB변경시 JPA에게 알려주면 이러한 종속성을 해결할 수 있습니다.

 

 


Spring Data JPA 란?

Spring에서 JPA를 사용할 때 JPA의 구현체들인 Hibernate, EclipseLink, DataNucleus를 직접 다루는 것이 아니라,

이 구현체들을 좀 더 쉽게 사용하고자 추상화시킨 스프링 진영에서 개발한 라이브러리로 Spring에서 JPA를 편리하게 사용할 수 있게 도와줍니다.

 

build.gradle에 Spring Data JPA 의존성 추가 
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

 

JpaRepository<> 인터페이스만 상속받으면 스프링 데이터 JPA가 프록시 기술을 사용해서 구현 클래스를 대신 만들어 주고,

만든 구현 클래스의 인스턴스를 만들어서 스프링 빈으로 등록합니다.

따라서 개발자는 구현 클래스 없이 인터페이스만 만들면 기본 CRUD 기능을 사용할 수 있습니다.

public interface ItemRepository extends JpaRepository<Item, Long> {
}

위 처럼, JpaRepository 인터페이스를 인터페이스로 상속 받고, JpaRepository의 제네릭에 관리할 <객체, id 타입>을 주면 JpaRepository 가 제공하는 CRUD 기능들을 모두 사용할 수 있습니다.

 

실제로 JpaRepository 인터페이스를 열어 보면 아래와 같이 기능들이 정의되어 있습니다. (사진은 일부 코드)


쿼리 메서드 기능

스프링 데이터 JPA는 인터페이스에 메서드만 적어두면, 메서드 이름을 분석해서 JPQL 쿼리를 자동으로 만들고 실행해주는 기능을 제공합니다.

public interface MemberRepository extends JpaRepository<Member, Long> {
 	List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}

 

물론 인공지능이 아니기 때문에 아무런 규칙없이 작동하진 않고 아래와 같은 규칙이 있습니다.

조회: find…By , read…By , query…By , get…By
예:) findHelloBy 처럼 ...에 식별하기 위한 내용(설명)이 들어가도 된다.

COUNT: count…By (반환타입 long)
EXISTS: exists…By (반환타입 boolean)

삭제: delete…By , remove…By (반환타입 long)
DISTINCT: findDistinct , findMemberDistinctBy
LIMIT: findFirst3 , findFirst , findTop , findTop3

 

쿼리 메소드 필터 조건 자세히 알아 보기

 

JPA 쿼리 메서드(Query Method)

자동 생성 쿼리 메소드의 명명 규칙

www.devkuma.com

 


JPQL 직접 사용하기

쿼리 메서드 기능 대신에 직접 JPQL을 사용하고 싶을 때는 @Query 와 함께 JPQL을 작성할 수 있습니다.

이때 메서드 이름으로 실행하는 규칙은 무시됩니다.

 

아래처럼 JPQL 쿼리 문법으로 작성하고 파라미터로 넘기는 필드는 @Param 어노테이션으로 매칭시켜줘야 합니다. 

public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long> {
     //쿼리 메서드 기능
     List<Item> findByItemNameLike(String itemName);

     //쿼리 직접 실행
     @Query("select i from Item i where i.itemName like :itemName and i.price <= :price")
     List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);
}