JPA 02 - How to Use JPA


JPA 설정

JPA는 자바 애플리케이션에서 RDBMS와의 상호작용을 쉽게 처리할 수 있도록 도와주는 자바 표준 API이다. 이를 사용하기 위해선 몇가지 설정 과정이 필요하다.

1. 프로젝트 의존성 추가

먼저 작업하려는 프로젝트에 JPA 관련 의존성을 추가해줘야 한다. Gradle 또는 Maven을 통해 설정할 수 있다. 위의 코드는 maven의 pom.xml로 설정하는 방법이고, 아래 코드는 build.gradle로 설정하는 방법이다.

<dependencies>
    <!-- JPA 의존성 -->
    <dependency>
        <groupId>jakarta.persistence</groupId>
        <artifactId>jakarta.persistence-api</artifactId>
        <version>3.0.0</version>
    </dependency>

    <!-- Hibernate(JPA 구현체) 의존성 -->
    <dependency>
        <groupId>org.hibernate.orm</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>6.0.0.Final</version>
    </dependency>

    <!-- H2 Database 의존성 (테스트용) -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>
plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    // JPA 의존성
    implementation 'jakarta.persistence:jakarta.persistence-api:3.0.0'

    // Hibernate (JPA 구현체)
    implementation 'org.hibernate.orm:hibernate-core:6.0.0.Final'

    // H2 Database (테스트용)
    runtimeOnly 'com.h2database:h2'
}

tasks.named('test') {
    useJUnitPlatform()
}

2. 데이터베이스 연결 설정

JPA 의존성을 설정한 후엔 사용할 데이터베이스에 대한 연결 설정도 해줘야 한다. 그렬려면 프로젝트 구조상 main의 resources에 있는 META-INF/persistence.xml 파일을 생성하여 설정하거나, 스프링 부트의 경우 application.properties 파일에 설정할 수 있다.

<!-- META-INF/persistence.xml -->

<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
             https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
             version="3.0">
    <persistence-unit name="example-unit">
        <class>com.example.entity.Member</class>
        <properties>
            <!-- 데이터베이스 설정 -->
            <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:testdb"/>
            <property name="jakarta.persistence.jdbc.user" value="sa"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>

            <!-- Hibernate 설정 -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>
# application.properties

# H2 데이터베이스 설정
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA Hibernate 설정
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

자세한 설정 값에 대한 설명은 다음과 같다.

  • spring.datasource.url=jdbc:h2:mem:testdb
    • 데이터베이스 접속 URL
  • spring.datasource.driverClassName=org.h2.Driver
    • .JDBC 드라이버
  • spring.datasource.username=sa
    • 데이터베이스 접속 아이디
  • spring.datasource.password=
    • 데이터베이스 접속 비밀번호
  • spring.jpa.properties.hibernate.dialect
    • hibernate 의 사용 방언 설정(es MySQL, Oracle SQL, h2 SQL 등)

엔티티

설정이 완료되었으면 이제 본격적으로 JPA르 사용해본다. DB에서 구성된 테이블이 자바에선 클래스로 매핑이 된다 이를 엔티티라 부르며 이를 사용한 클래스가 엔티티 클래스라 한다.

package com.mentoring.webboard.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;

import java.time.LocalDateTime;

@Entity
@Table(name = "NAME")
@Data
public class Board {
    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "TITLE")
    private String title;
    private String content;
    private String writer;
    private LocalDateTime createdDate = LocalDateTime.now();

    // Getter, Setter, Constructors
}

위에는 엔티티 클래스의 예시이다. 이 예시에 나와있는 어노테이션은 다음과 같다.

  • @Entity: 해당 클래스가 DB 테이블과 매핑이 되는 엔티티 클래스임을 선언
  • @Table(name = “NAME”): 매핑된 테이블의 이름 설정, 없으면 클래스 이름이 테이블 이름으로 매핑이 됨
  • @Id: 매핑되는 테이블의 기본 키를 선언한다.
  • @GeneratedValue(strategy = GenerationType.IDENTITY): 생성시 적용되는 값의 전략을 설정한다. 여기선 고유적인 값으로 설정이 된다.
  • @Column(name = “ID”): 클래스 필드가 테이블의 어느 컬럼과 매핑되는지 선언한다. 없으면 필드 명이 컬럼 명으로 매핑이 된다.
  • @Data: Lombok 라이브러리용 어노테이션으로 사용시 해당 클래스의 getter/setter가 선언 안 해도 자동으로 접근 가능하며, 기본 생성자 또한 선언 안 해도 접근 가능하게 만든다.

엔티티 매니저

엔티티 클래스로 매핑된 데이터, 즉 엔티티는 엔티티 매니저라는 곳을 통해 DB에 CRUD작업을 진행할 수 있다. 그리고 엔티티 매니저는 JPA 설정 정보를 통해서 만든 엔티티 매니저 팩토리라는 곳에서 만들어진다. 아래 실제로 사용하는 예를 들어 설명한다.

public class JpaExample {
    public static void main(String[] args) {
        // 엔티티 매니터 팩토리 생성
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit");

        // 엔티티 매니터 생성
        EntityManager em = emf.createEntityManager();

        // 트랜잭션 획득
        EntityTransaction tx = em.getTransaction();

        try{
            // 트랜잭션 시작
            tx.begin();

            // 비즈니스 로직 수행
            logic(tx);

            // 트랜잭션 커밋
            tx.commit();

        } catch (Exception e){
            // 트랜잭션 롤백
            tx.rollback();

        } finally {
            // 엔티티 매니터 종료
            em.close();

        }
        // 엔티티 매니터 팩토리 종료
        emf.close();
    }
}

위의 코드에 따라 엔티티 사용 과정은 다음과 같다.

  1. 엔티티 매니저 팩토리 생성: 수동으로 설정한 persistence-unit 값에 따른 설정으로 엔티티 메니저 팩토리 생성하거나, 스프링 부트에선 자동으로 만들어 준다.
  2. 엔티티 매니터 생성: 생성된 엔티티 매니저 팩토리로부터 생성
  3. 트랜잭션 작업 시작: DB의 작업 단위를 트랜잭션이라 한다. 즉 작업 단위의 시작을 선포한다.
  4. 비즈니스 로직 수행: 의도하는 작업을 트랜잭션 내에서 실행
  5. 커밋 또는 롤백: 모든 비즈니스 로직이 완료되었으면 커밋, 의도치 않게 안되었다면 롤백을 하여 작업 단위 종료
  6. 엔티티 매니저 종료
  7. 엔티티 매니저 팩토리 종료

JPA의 CRUD

엔티티 매니저 및 엔티티에 대한 개념을 알았다면, 이제 비즈니스 로직상에서 JPA를 통해 어떻게 사용되는지를 기본적인 CRUD로 구분해서 알아본다.

public static void logic(EntityTransaction tx) {
    EntityManager em = tx.getEntityManager();

    // 1. 엔티티 생성 저장 (Create)
    Member newMember = new Member("John");
    em.persist(newMember);  // persist 메소드를 사용해 저장

    // 2. 엔티티 조회 (Read)
    Member foundMember = em.find(Member.class, newMember.getId()); // find 메서드를 사용해 찾기
    System.out.println("Found Member: " + foundMember.getName());

    // 여러 명의 Member 조회 (JPQL 사용)
    List<Member> members = em.createQuery("SELECT m FROM Member m", Member.class).getResultList();

    // 3. 엔티티 수정 (Update)
    foundMember.setName("John Doe");
    em.merge(foundMember);  // merge 메소드를 사용해 변경 사항을 반영

    // 4. 엔티티 삭제 (Delete)
    em.remove(foundMember);  // 엔티티 삭제
}
  1. 엔티티 생성 저장 (Create)
    엔티티 객체를 생성한 후 엔티티 매니저의 persist() 메소드에 넘겨서 저장
  2. 엔티티 조회 (Read)
    엔티티 매니저의 find() 메소드를 통하여 원하고자 하는 객체를 조회.
    여러 개 찾으려면 JPA내 직접 sql문을 이용한다. 이를 JPQL(Java Persistence Query Language)라 하며, 엔티티 매니저의 createQuery() 메서드내 JPQL을 넘겨서 실행한 후 getResultList() 메서드로 결과를 가져온다.
  3. 엔티티 수정 (Update)
    바꾼 엔티티를 엔티티 매니저의 merge() 메소드를 통해 변경
  4. 엔티티 삭제 (Delete)
    삭제할 엔티티 객체를 엔티티 매니저의 remove() 메서드르 통해 삭제