DEV ℧ Developer Diary

[JPA] JPA의 간단한 예시

해당 포스트는 인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 을 듣고 정리한 글입니다.

JPA를 이용해 간단한 예시를 만들어보자.

프로젝트 환경

  • Spring boot
  • gradle
  • h2 database

gradle

먼저 JPA와 h2 database 관련 의존성을 설정해 줍니다.

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'

persistence.xml

JPA를 이용하기 위해서는 해당 xml 설정을 주가해 주어야 합니다. 만약 Spring boot를 이용해 내장 WAS를 실행했다면, application.yml에 설정하는 것으로 Spring boot가 알아서 설정을 진행해줍니다.

하지만 해당 내용에서는 main 메서드를 만들어 실행하기 때문에 persistence.xml를 만들어 적용하였습니다.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="hello">
        <class>com.dhaudgkr.jpastart.hellojpa.entity.Member</class>
        <properties>
            <!-- JPA 하이버네이트 -->
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <!-- H2 데이터베이스 -->

            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
            <!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
        </properties>
    </persistence-unit>
</persistence>
  • hibernate.dialect : Database 방언(Dialect)
    • Database 방언은 SQL 문법 차이를 말하며, JPA 구현체들은 이를 보정해주기 위해 Dialect Class를 제공한다. H2Dialect, Oracle10gDialect, MySQLDialect 등등 데이터베이스의 종류에 맞게 설정이 가능하다.
  • hibernate.show_sql : JPA가 실행한 sql 조회 옵션
  • hibernate.format_sql : 조회된 SQL 쿼리 포맷팅 옵션
  • hibernate.use_sql_comments : JPA가 실행한 sql의 주석 조회 옵션

JPA 사용해보기

Entity를 만들어 줍니다.

  • @Entity

    @Entity가 붙은 클래스는 JPA가 관리하는 클래스임을 명시합니다.

  • @Id

    해당 테이블의 PK를 명시합니다.

  • @Table

    Entity와 DB table를 매핑해줍니다.

  • @Column

    Entity의 변수와 table의 컬럼을 매핑해줍니다.

@Entity
@Table(name = "USER")
public class Member {

    /* 테이블의 ID 컬럼 설정 */
    @Id
    private Long id;

    /* 해당 변수의 컬럼명 매칭 */
    @Column(name = "username")
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

EntityManagerFactory와 EntityManager

  • 구조

EntityManagerFactory와 EntityManager

EntityManagerFactory에서 요청이 올때마다 EntityManager를 생성한다. EntityManager 내부적으로 DB의 ConnectionPool을 이용해서 JPA에서 데이터를 불러온다.

  • EntityManagerFactory
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");

Entity Manager를 생성하기 위해 위에서 설정한 persistence.xml 파일의 hello 설정을 EntityManagerFactory에 불러온다. EntityManagerFactory의 생성 비용은 크기 때문에, 애플리케이션 전체에 한번만 실행을 시켜줘야 하며, 애플리케이션 종료시 entityManagerFactory.close(); 를 통해 닫아 주어야 한다.

  • EntityManager
EntityManager entityManager = entityManagerFactory.createEntityManager();

EntityManager는 Entity를 관리하며, DB의 ConnectionPool과 동일하게 생각하면 편하게 이해할 수 있다. EntityManager는 DB Connection과 밀접한 관계가 있어, 스레드간 공유하거나 재사용하면 안된다, entityManager.close();로 닫아줘야한다.

  • EntityTransaction
EntityTransaction entityTransaction = entityManager.getTransaction();

EntityManager에서 트랜잭션 정보를 가져온다. JPA의 모든 데이터 변경은 트랜잭션 안에서 실행한다.

JPA의 CRUD

INSERT

public void insertJpa() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction entityTransaction = entityManager.getTransaction();
    entityTransaction.begin();
    try {
        Member member = new Member();
        member.setId(3L);
        member.setName("Hello JPA");

        entityManager.persist(member);
        entityTransaction.commit();
    } catch (Exception e) {
        entityTransaction.rollback();
    } finally {
        entityManager.close();
    }
    entityManagerFactory.close();
}
/* insert com.dhaudgkr.jpastart.hellojpa.entity.Member
    */ insert
    into
        Member
        (name, id)
    values
        (?, ?)

해당 Member entity의 데이터를 넣어주면 JPA가 INSERT 쿼리를 만들어 실행한다.

SELECT

public void selectJpa() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction entityTransaction = entityManager.getTransaction();
    entityTransaction.begin();

    try {
        Member findMember = entityManager.find(Member.class, 1L);
        log.debug("findMember.id = " + findMember.getId());
        log.debug("findMember.name = " + findMember.getName());

        entityTransaction.commit();
    } catch (Exception e) {
        entityTransaction.rollback();
    } finally {
        entityManager.close();
    }
    entityManagerFactory.close();
}
select
      member0_.id as id1_0_0_,
      member0_.name as name2_0_0_
  from
      Member member0_
  where
      member0_.id=?
03:19:09.777 [main] DEBUG com.dhaudgkr.jpastart.hellojpa.JpaMain - findMember.id = 1
03:19:09.778 [main] DEBUG com.dhaudgkr.jpastart.hellojpa.JpaMain - findMember.name = HelloJPA

입력한 ID값으로 Member Table에서 JPA가 SELECT 쿼리를 만들어 Entity를 조회해온다.

UPDATE

public void updateJpa() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction entityTransaction = entityManager.getTransaction();
    entityTransaction.begin();

    try {
        Member findMember = entityManager.find(Member.class, 1L);
        /* Entity의 변경을 확인하고 JPA가 자체적으로 update 쿼리를 만들어서 날린다. */
        findMember.setName("HelloJPA");

        entityTransaction.commit();
    } catch (Exception e) {
        entityTransaction.rollback();
    } finally {
        entityManager.close();
    }
    entityManagerFactory.close();
}
/* update
    com.dhaudgkr.jpastart.hellojpa.entity.Member */ update
        Member
    set
        name=?
    where
        id=?

Entity의 속성을 변경하는 것만으로 JPA가 알아서 UPDATE 쿼리를 만들어 데이터를 변경한다.

DELETE

public void removeJpa() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction entityTransaction = entityManager.getTransaction();
    entityTransaction.begin();

    try {
        Member findMember = entityManager.find(Member.class, 1L);
        entityManager.remove(findMember);

        entityTransaction.commit();
    } catch (Exception e) {
        entityTransaction.rollback();
    } finally {
        entityManager.close();
    }
    entityManagerFactory.close();
}
/* delete com.dhaudgkr.jpastart.hellojpa.entity.Member */
delete
  from
      Member
  where
      id=?

입력한 ID값으로 Member Table에서 JPA가 DELETE 쿼리를 만들어 데이터를 삭제한다.