02. JPA 시작

01. Hello JPA - 프로젝트 생성

1. H2 데이터베이스 설치와 실행

2. 메이븐 소개

3. 프로젝트 생성

4. 라이브러리 추가 pom.xml

5. JPA 설정하기 - persistence.xml

6. 데이터베이스 방언

- JPA는 특정 데이터베이스에 종속 X
- 각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다름
- 방언: SQL 표준을 따르지 않는 각 DB만의 고유한 기능
	- ex) MySQL, Oracle SQL, H2 SQL
- hibernate.dialect 속성에 지정 (각각의 DB특성에 매핑되어 있음)

02. Hello JPA - 애플리케이션 개발

1. JPA 구동 방식

    1. Persistence Class에서 META-INF/persistence.xml 설정 정보를 읽음.
    1. EntityManagerFactory Class 생성
    1. 필요할때마다 EntityManagerFactory에서 EntityManger 생성

2. JPA 동작 확인

  • JpaMain 클래스 생성
    객체와 테이블을 생성하고 매핑하기
    - @Entity: JPA가 관리할 객체
    - @Id: DB PK와 매핑
create table Member( 
    id bigint not null,
    name varchar(255),
    primary key (id)
);
package hellojpa;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Member {

    @Id
    private Long id;
    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 애플리케이션 로딩 시점에 딱 하나만 만들어야 함. 실제 DB에 저장하는 트랜잭션 단위 DB 커넥션을 얻어서 쿼리를 날려서 종료되는 일관적인 작업을 할때마다 EntityManager를 생성해야 함.
package hellojpa;

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

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        Member member = new Member();

        em.persist(member); // 저장

        em.close();
        emf.close();
    }
}

error: ids for this class must be manually assigned

package hellojpa;

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

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        Member member = new Member();

        member.setId(1L);
        member.setName("HelloA");

        em.persist(member); // 저장

        em.close();
        emf.close();
    }
}

JPA에서 트랜잭션 단위가 정말 중요함.
모든 데이터를 변경하는 모든 작업은 트랜잭션 안에서 작업을 해야함

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

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

        em.persist(member); // 저장

        tx.commit(); // DB 커밋

        em.close();
        emf.close();
    }
}
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) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

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

        em.persist(member); // 저장

        tx.commit(); // DB 커밋

        em.close();
        emf.close();
    }
}

정상적으로 SQL 쿼리문이 실행되는 것을 확인할 수 있음.

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

실제 DB에서 쿼리문을 날려서 실행을 해보면 정상적으로 저장이 된 것을 확인 할 수 있음

값을 하나 더 넣어주면 저장이 잘 되는 것을 확인 가능

        member.setId(2L);
        member.setName("HelloB");


쿼리를 만든 적이 없고 JPA가 매핑 정보를 보고 알아서 넣어줌
정석 코드

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

        try {
            Member member = new Member();
            member.setId(2L);
            member.setName("HelloB");

            em.persist(member); // 저장

            tx.commit(); // DB 커밋

        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}
  • 조회
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) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

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

            tx.commit(); // DB 커밋

        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}

실행을 하면 다음과 같이 쿼리문이 나오는 걸 확인할 수 있음.

Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?
findMember.id = 1
findMember.name = HelloA
  • 삭제
    em.remove();
  • 수정
public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

        try {
            Member findMember = em.find(Member.class, 1L);
            findMember.setName("HelloJPA");
            
            tx.commit(); // DB 커밋

        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}

실행 결과: Update 쿼리문이 실행 되는 걸 확인할 수 있음.

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=?

실제 DB에서 조회를 해보면 값이 변경된 것을 확인할 수 있음.

why?

  • JPA를 통해서 Entity를 가져오면 JPA가 관리를 함.
  • JPA가 변경이 되었는지 트랜잭션을 commit하는 시점에서 체크를 함.
  • 바뀌었으면 트랜잭션 commit 하기 직전에 Update 쿼리를 만들어 날리고 트랜잭션이 커밋됨.

주의

  • EntityManagerFactory는 웹서버가 올라오는 시점에서 DB당 하나가 생성이 됨
  • EntityManger라는 것은 고객의 요청이 올때마다 계속 사용했다가 버렸다가 동작
    조심해야 할 부분: 쓰레드간에 절대 공유해서는 안됨 - 장애가 발생(사용하고 버려야 한다.)
  • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
  • 내부적으로 DB가 트랜잭션 쿼리를 다 처리

JPQL 소개

  • 가장 단순한 조회 방법
    - EntityManager.find()
    - 객체 그래프 탐색(a.getB().getC())
  • 나이가 18살 이상인 회원을 모두 검색하고 싶다면?
public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

        try {
//            Member findMember = em.find(Member.class, 1L);
//            findMember.setName("HelloJPA");
            List<Member> result = em.createQuery("select m from Member as m", Member.class)
                    .getResultList();

            for (Member member : result) {
                System.out.println("member.names = " + member.getName());
            }
            tx.commit(); // DB 커밋

        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}

아래와 같은 결과값이 나오는 것을 확인할 수 있다.

Hibernate: 
    /* select
        m 
    from
        Member as m */ select
            member0_.id as id1_0_,
            member0_.name as name2_0_ 
        from
            Member member0_
member.names = HelloJPA
member.names = HelloB

이거의 장점이 뭐지?

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

        try {
//            Member findMember = em.find(Member.class, 1L);
//            findMember.setName("HelloJPA");
            List<Member> result = em.createQuery("select m from Member as m", Member.class)
                    .setFirstResult(5)
                    .setMaxResults(10)
                    .getResultList();

            for (Member member : result) {
                System.out.println("member.names = " + member.getName());
            }
            tx.commit(); // DB 커밋

        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}

JPQL: 객체를 대상으로 하는 객체지향 쿼리이기 때문에 DB의 방언에 맞게 매핑을 해서 쿼리문을 날려줌.

package hellojpa;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        //code
        EntityTransaction tx = em.getTransaction(); // 트랜잭션 얻을 수 있음
        tx.begin(); // DB 트랜잭션 시작

        try {
//            Member findMember = em.find(Member.class, 1L);
//            findMember.setName("HelloJPA");
            List<Member> result = em.createQuery("select m from Member as m", Member.class)
                    .setFirstResult(5)
                    .setMaxResults(10)
                    .getResultList();

            for (Member member : result) {
                System.out.println("member.names = " + member.getName());
            }
            tx.commit(); // DB 커밋

        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}


실습 - JPQL 소개
• JPQL로 전체 회원 검색
• JPQL로 ID가 2 이상인 회원만 검색
• JPQL로 이름이 같은 회원만 검색
• JPQL에 대해 자세한 내용은 객체지향 쿼리에서 학습
  
JPA를 사용하면 엔티티 객체를 중심으로 개발
• 문제는 검색 쿼리
• 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
  (why? 테이블을 가져오면 JPA의 사상이 깨지니까)
• 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
• 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검
색 조건이 포함된 SQL이 필요(실제 RDB에 있는 물리적인 DB에 쿼리를 날리면 그 DB에 종속적으로 설계가 되기 때문에 엔티티 객체를 대상으로 하는 쿼리를 날리는 JPQL 사용)
  
JPQL
• JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
• SQL과 문법 유사, SELECT, FROM, WHERE, GROUP BY,
HAVING, JOIN 지원
• JPQL은 엔티티 객체를 대상으로 쿼리
• SQL은 데이터베이스 테이블을 대상으로 쿼리

테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리
• SQL을 추상화해서 특정 데이터베이스 SQL에 의존X
• JPQL을 한마디로 정의하면 객체 지향 SQL
• JPQL은 뒤에서 아주 자세히 다룸
- Jpa 동작 확인

좋은 웹페이지 즐겨찾기