2. JPA 시작


1. 객체 매핑 시작

  • 클래스와 테이블을 매핑하기 위해 어노테이션을 사용함
  • import javax.persistence.*
    
    @Entity
    @Table(name="MEMBER")
    public class Member{
    
      @Id
      @Column(name = "ID")
      private String id;
    
      @Column(name = "NAME")
      private String username;
    
      // 매핑 정보가 없는 필드
      private Integer age;
    
    }
    
  • 1
  • @Entity
    • 해당 클래스를 테이블과 매핑한다고 JPA에게 알려줌
    • @Entity 어노테이션이 사용된 클래스를 엔티티 클래스라고 함
  • @Table
    • 엔티티 클래스에 매핑할 테이블 정보를 알려줌
    • 위에서는 name 속성을 사용해서 Member 클래스를 MEMBER 테이블에 매핑함
    • 이 어노테이션을 생략하면 클래스 이름을테이블 이름으로 매핑함(정확히는 엔티티 이름을 사용)
  • @Id
    • 엔티티 클래스의 필드를 테이블의 기본 키Primary key에 매핑한다
    • @Id 어노테이션이 사용된 필드를 식별자 필드라고 함
  • @Column
    • 필드를 컬럼에 매핑함
    • 여기서는 name 속성을 사용해서 Member 엔티티의 username 필드를 MEMBER 테이블의 NAME 컬럼에 매핑했음
  • 매핑 정보가 없는 필드
    • 위에서 age 필드와 같이 어노테이션을 생략한 필드는 필드명을 사용해서 컬럼명으로 매핑함
    • 대소문자를 구분하는 데이터베이스를 사용하면 명시적으로 매핑해야 함@Column(name="AGE")

2. persistence.xml 설정

  • JPA는 persistence.xml을 사용해서 필요한 설정 정보를 관리함
  • META-INF/persistence.xml 클래스 패스 경로에 있으면 별도의 설정 없이 JPA가 인식 가능함
  <!-- 전체 코드 -->
  <?xml version="1.0" encoding="UTF-8"?>
  <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
    <persistence-unit name="jpabook">
      <properties>
        <!-- 필수 속성 -->
        <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"/>

        <!-- 옵션 -->

        <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.id.new_generator_mappings" value="true"/>
      </properties>
    </persistence-unit>
  </persistence>
  • <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
    
    • XML 네임스페이스와 사용할 버전을 지정, 위와 같이 2.1로 지정
  •  <persistence-unit name="jpabook">
    
    • JPA 설정은 영속성 유닛persistence-unit 부터 시작
    • 일반적으로 연결할 데이터베이스당 하나의 영속성 유닛을 등록
    • “jpabook”과 같은 고유한 이름을 부여해야 함
  • <properties>
      <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/> 
    
    • 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"/>  
        
    • 하이버네이트 속성
      • hibernate,dialect : 데이터베이스 방언dialect 설정

3. 데이터베이스 방언(Dialect)

  • 데이터베이스 방언이란?
    • SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능 또는 형식
      • 데이터타입 : MySQL에서는 VARCHAR, 오라클은 VARCHAR2
      • 다른 함수명 : 문자열을 자르는 함수를 SQL 표준에서는 SUBSTRING(), 오라클은 SUBSTR() 사용
      • 페이징처리 : MySQL은 LIMIT 사용, 오라클은 ROWNUM 사용
  • 데이터베이스의 종속성이 커져서 결국 데이터베이스 교체가 어려워짐
  • 하이버네이트를 포함한 대부분의 JPA 구현체들은 이런 문제를 해결하기 위해 데이터베이스 방언 클래스를 제공함
  • 2
  • H2 : org.hibernate.dialect.H2Dialect
  • 오라클 10g : org.hibernate.dialect.Oracle10gDialect
  • MySQL : org.hibernate.dialect.MySQL5InnoDBDialect

4. 애플리케이션 개발

  public static void Main(String args[]){
    // [엔티티 매니저 팩토리] - 생성
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
    // [엔티티 매니저] - 생성
    EntityManager em = emf.createEntityManager();
    // [트랜잭션] - 획득
    EntityTransaction tx = em.getTransaction();
        
    try {
      tx.begin();              // [트랜잭션] - 시작
      logic(em);               // 비즈니스 로직 실행
      tx.commit();             // [트랜잭션] - 커밋
    } catch (Exception e) {
        tx.rollback();          // [트랜잭션] - 롤백
    } finally {
        em.close();            // [엔티티 매니저] - 종료
    }
    emf.close();               // [엔티티 매니저 팩토리] - 종료
  }
  • 크게 세 부분으로 나뉘어짐
    • 엔티티 매니저 설정
    • 트랜잭션 관리
    • 비즈니스 로직


4.1 엔티티 매니저 설정

  • 엔티티 매니저 팩토리 설정
    • 3
    • 엔티티 매니저 팩토리 생성
      • EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
        
      • persistence.xml에서 이름이 jpabook인 영속성 유닛persistence-unit을 찾아서 엔티티 매니저 팩토리를 생성함
      • 시간이 오래걸림
        • persistence.xml을 읽고, JPA를 동작시키기 위한 기반 객체를 만들고, JPA 구현체에 따라서는 DB 커넥션 풀도 생성함
      • 엔티티 매니저 팩토리는 애플리케이션 전체에서 딱 한 번만 생성하고 공유해서 사용해야 함
  • 엔티티 매니저 생성
    • EntityManager em = emf.createEntityManager();
      
    • 엔티티 매니저를 통해 엔티티를 데이터베이스에 등록/조회/수정/삭제 할 수 있음
    • 내부에 데이터베이스 커넥션을 유지하면서 데이터베이스와 통신함
    • 데이터베이스 커넥션과 밀접하기 때문에 스레드 간에 공유하거나 재사용하면 안됨
    • 엔티티 매니저 팩토리로부터 생성되고 JPA의 대부분의 기능을 제공함
  • 종료
    • em.close();
      emf.close();
      
    • 사용이 끝난 엔티티 매니저는 반드시 종료(close) 해야함
    • 애플리케이션을 종료할 때 엔티티 매니저 팩토리도 반드시 종료(close) 해야함


4.2 트랜잭션 관리

JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 함, 트랜잭션 없이 데이터 변경 시 예외 발생함

  EntityTransaction tx = em.getTransaction(); // 트랜잭션 API
  try{
    tx.begin();              // 트랜잭션 시작
    logic(em);
    tx.commit();             // 트랜잭션 커밋
  }catch(Eception e){
    tx.rollback();           // 예외 발생 시 트랜잭션 롤백
  }

위에처럼 엔티티 매니저(em)에서 트랜잭선 API(tx)를 받아와야 함


4.3 비즈니스 로직

엔티티 매니저를 이용해서 CRUD로 비즈니스 로직 구현하면 됨

  • 등록(Create)
    • Member member = new Member();
      member.setId(id);
      member.setUsername()
      em.persist(member);
      
    • JPA는 회원 엔티티의 매핑 정보(어노테이션(을 분석해서 다음과 같은 SQL을 만들어 쿼리해줌
    • INSERT INTO MEMBER (ID, NAME) VALUES ('id1', 'name1')
      
  • 조회(Read)
    • // 한 건 조회
      Member findMember = em.find(Member.class, id);
      // 목록 조회
      List<Member> members =
            em.createQuery("select m from Member m", Member.class)
            .getResultList();
      
    • find() 메소드는 조회할 엔티티 타입(class)과 데이터베이스 기본 키와 매핑한 식별자 값(@Id)으로 엔티티 하나를 조회해줌
    • JPQL을 사용해서 검색 조건을 특정할 수 있음
  • 수정(Update)
    • member.setName("newname");
      
    • 단순히 엔티티 객체의 값만 변경해줘도 DB에 아래와 같은 업데이트 쿼리를 알아서 해줌
    • UPDATE MEMBER SET NAME="newname" WHERE ID="id1"
      
  • 삭제(Delete)
    • em.remove(member);
      
    • 엔티티 매니저의 remove() 메소드 호출을 통해 엔티티 삭제가 가능함
    • DELETE FROM MEMBER WHERE ID="id1
      


4.4 JPQL

위에 조회를 하는 코드 중 하나 이상의 회원을 조회할 때의 코드를 보면

  •   // 목록 조회
      List<Member> members =
            em.createQuery("select m from Member m", Member.class)
            .getResultList();
    
  • JPA를 사용하면 엔티티 객체 중심의 개발이 가장 큰 장점, 데이터베이스에 대한 처리는 JPA에게 맡겨야 함
  • 원래는 조회를 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색해야 함(컬렉션 사용하듯이)
  • 그러려면 테이블의 모든 엔티티들을 불러와서 엔티티 객체로 만들어야 검색이 가능해짐
  • 애플리케이션에서 필요로 하는 데이터만 불러오기 위해 결국 검색 조건이 포함된 SQL(=JPQL)을 사용해야 함

  • JPQL?
    • SQL을 추상화한 객체지향 쿼리 언어
    • SQL과 비슷한 문법을 가지며 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 등 사용 가능
    • JPQL vs SQL
      • JPQL운 엔티티 객체를 대상으로 쿼리, 즉 클래스와 필드를 대상으로 쿼리함
      • SQL은 데이터베이스 테이블을 대상으로 쿼리함
    • 위에 조회 코드에서 아래 SQL문 처럼 생긴 부분이 JPQL
    • select m from Member m
    • 여기서 Member는 엔티티 객체를 뜻하는 것이고, 데이터베이스의 MEMBER 테이블을 의미하는 것이 아님
    • JPQL은 엔티티 객체를 대상으로 쿼리하는 것이기 때문에 데이터베이스 테이블과는 독립적
    • 사용 방법?
      • 엔티티 매니저의 createQuery(JPQL, Type) 호출을 통해 쿼리 객체 생성
      • 생성한 쿼리 객체의 getResultList() 메소드 호출





© 2020.02. by blupine