✅JPA 실습

👏라이브러리 버전 선택

Spring Framework에서 라이브러리 버전 선택할 때,
https://spring.io/projects/spring-boot#learn 에서
현재 Spring Boot 버전 👉 Reference Doc. 👉 Dependency Versions 에서 명시된 버전으로 설정한다.

👏persistence.xml 파일

/src/main/resource/META-INF/ 위치에 persistence.xml 파일을 둔다
<persistenceversion="2.2" : JPA 버전이 2.2
<persistence-unitname="hello"> : hello 라는 이름의 데이터베이스를 만든다
필수 속성 : 드라이버, 유저네임, 패스워드, url, dialect(방언)를 설정한다. 👉 JPA는 특정 데이터베이스 방언에 종속되어있지 않다.

✍ 데이터베이스 방언

✅JPA 어플리케이션 개발

META-INF 폴더 속 persistence.xml 파일을 읽어서 EntityManagerFactory 클래스를 만든다.
필요할 때마다 EntityManagerFactory 클래스에서 EntityManager 를 생성하여 사용한다.

👏JPA 동작

EntityManagerFactory : 웹 서버가 올라오는 시점에 DB당 한 개만 생성된다.
EntityManager : 고객의 요청이 올 때마다 emf.createEntityManager()으로 생성되고 em.close로 버려진다. (쓰레드간에 공유해선 안된다. 사용하고 버려야 한다)
EntityTransaction : 👏JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.

실습 전에 먼저 Member 테이블와 DB의 Member 테이블과 매핑해줄 Member 엔티티 클래스를 생성해준다.

  • @Entity : JPA가 관리할 객체를 지정해주는 어노테이션
  • @Id : 엔티티 클래스와 연결된 테이블의 PK와 매핑시켜주는 어노테이션

✍INSERT - 삽입

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {
    public static void main(String[] args) {
        // <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
        // EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        // EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
        EntityManager em = emf.createEntityManager();

        // JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member member = new Member();
            member.setId(1L);
            member.setName("HelloA");

            em.persist(member);

            // 정상적으로 종료하면 commit
           tx.commit();
        } catch (Exception e) {
            // 비정상 종료시 rollback
            tx.rollback();
        } finally {
            // EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
            em.close();
        }

        // 어플리케이션이 끝나면 EntityManagerFactory를 닫는다다
       emf.close();
    }
}

Hibernate: 
    /* insert hellojpa.Member
        */ insert 
        into
            Member
            (name, id) 
        values
            (?, ?)

✍SELECT - 조회

        try {

            Member findMember = em.find(Member.class, 1L);
            System.out.println("findMember = " + findMember.getId());
            System.out.println("findMember = " + findMember.getName());

            // 정상적으로 종료하면 commit
            tx.commit();
        } catch (Exception e) {
            // 비정상 종료시 rollback
            tx.rollback();
        } finally {
            // EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
            em.close();
        }

Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?
findMember = 1
findMember = HelloA

✍DELETE - 삭제

        try {

            Member findMember = em.find(Member.class, 1L);

            em.remove(findMember);
            // 정상적으로 종료하면 commit
            tx.commit();
        } catch (Exception e) {
            // 비정상 종료시 rollback
            tx.rollback();
        } finally {
            // EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
            em.close();
        }

Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?
Hibernate: 
    /* delete hellojpa.Member */ delete 
        from
            Member 
        where
            id=?

✍UPDATE - 수정

        try {

            Member findMember = em.find(Member.class, 2L);
            findMember.setName("HelloJPA");
            // em.persist(findMember); -> persist로 저장해줄 필요 없다!! 👍

            // 정상적으로 종료하면 commit
            tx.commit();
        } catch (Exception e) {
            // 비정상 종료시 rollback
            tx.rollback();
        } finally {
            // EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
            em.close();
        }

Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?
Hibernate: 
    /* update
        hellojpa.Member */ update
            Member 
        set
            name=? 
        where
            id=?

findMember.setName("HelloJPA"); 를 실행하면 UPDATE query가 나간다.
👉 em.persist(findMember); 으로 따로 다시 저장해 줄 필요 없다
JPA가 트랜잭션을 commit하는 시점에서 Member 엔티티의 값이 변경되었는지 체크한다.
변경이 감지되면 commit 직전에 update query를 날린 후 commit된다.


👏JPQL

간단한 조회는 EntityManager.find() 또는 객체 그래프 탐색을 활용하면 된다.

하지만 나이가 18살 이상인 회원 검색과 같은 복잡한 조회가 필요할 경우
👉 JPQL을 활용한다.

        try {
            // qlString에서 Member는 테이블이 아니라 객체(엔티티 클래스)를 의미한다.
            List<Member> result = em.createQuery("select m from Member as m", Member.class)
                    .setFirstResult(0)
                    .setMaxResults(8) // setFirstResult ~ setMaxResults 함수로 페이징을 쉽게 구현할 수 있다
                    .getResultList();

            for (Member member : result) {
                System.out.println("member = " + member.getName());
            }
            // 정상적으로 종료하면 commit
            tx.commit();
        } catch (Exception e) {
            // 비정상 종료시 rollback
            tx.rollback();
        } finally {
            // EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
            em.close();
        }
        
Hibernate: 
    /* select
        m 
    from
        Member as m */ select
            member0_.id as id1_0_,
            member0_.name as name2_0_ 
        from
            Member member0_ limit ?
member = HelloJPA

✍JPQL 특징

  • JPA를 사용하면 엔티티 객체를 중심으로 개발
  • 문제는 검색 쿼리
  • 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
  • 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
  • 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요
  • JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
  • SQL과 문법 유사, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
  • 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리
  • SQL을 추상화해서 특정 데이터베이스 SQL에 의존X
  • JPQL을 한마디로 정의하면 객체 지향 SQL

✍SQL과 JPQL의 차이

  • JPQL : 엔티티 객체를 대상으로 쿼리 👉 관계형 DB의 방언을 바꾸어도 JPQL 코드를 변경할 필요가 없다
  • SQL : 데이터베이스 테이블을 대상으로 쿼리

0개의 댓글