JPA 시작

2. JPA 시작

2.1 IntelliJ 설치와 프로젝트 설정하기

  • 책에서는 이클립스를 사용하였으나 IntelliJ가 더 편하다고 생각되어 IntelliJ를 사용하여 프로젝트 설정하는 방법을 정리하였다.

  • IntelliJ 설치
    • IntelliJ
    • 커뮤니티 버전이 무료이므로 커뮤니티 버전으로 다운받는다.
  • 프로젝트 설정
    • start.spring.io
    • Dependencies에 H2 Database와 Spring Data JPA를 검색하여 추가한다.
    • 아래 화면과 같이 설정한 후 Generate 버튼을 클릭하여 다운받는다.
    • 다운받은 zip 파일 압축을 풀고 IntelliJ에서 압축 푼 폴더안의 build.gradle을 Open 한다. Open As Project를 선택한다.

2.2 H2 데이터베이스 설치

  • H2 Database
  • OS에 맞는 버전을 설치해준다.
  • Mac 기준으로 bin 폴더의 h2.sh를 실행하면 데이터베이스가 실행되면서 웹브라우저에 아래와 같이 접속창이 뜬다.
  • 최초 1회는 데이터베이스 생성을 위해 아래 주소와 같이 설정하여 연결을 클릭한다.
  • 이후부터는 아래와 같이 localhost로 접속한다.

2.3 라이브러리와 프로젝트 구조

  • 책에서는 라이브러리 관리 도구로 메이븐을 사용하지만, Gradle을 사용하도록 한다.
  • Gradle은 build.gradle 파일에 필요한 라이브러리를 추가하면 된다.
  • 필요한 라이브러리는 MVN Repository 에서 찾으면 된다.
  • 예를 들어, Spring JDBC를 추가할 경우 아래와 같이 찾은 뒤, 텍스트 박스의 내용을 build.gradle 파일의 dependencies 에 추가한다. 그리고 우측 상단의 코끼리 아이콘이 생기면 클릭하여 Load 해준다.
  • 예제를 실행하는데 필요한 라이브러리는 com.h2database 와 org.springframework.boot:spring-boot-starter-data-jpa 두 개면 충분한 것 같다.

2.4 객체 매핑 시작

  • H2 데이터베이스에서 아래와 같이 SQL을 실행해서 회원 테이블을 만든다.
  • Member 클래스를 만들고 매핑 어노테이션을 추가한다.
  • @Entity
    • 클래스를 테이블과 매핑한다고 알려준다.
    • @Entitiy가 사용된 클래스를 엔티티 클래스라고 한다.
  • @Table
    • 테이블 정보를 알려준다. name 속성으로 테이블 이름을 설정한다.
  • @Id
    • 엔티티 클래스의 필드를 테이블의 기본 키(PK)에 매핑한다.
  • @Column
    • 필드를 컬럼에 매핑한다. name 속성으로 테이블의 컬럼이름을 설정한다.
  • 매핑 정보가 없는 필드
    • 필드명을 사용해서 컬럼명으로 매핑한다.
@Entity
@Table(name = "member")
public class Member {
    @Id
    @Column(name="id")
    private String id;
    @Column(name = "name")
    private String username;
    private Integer age;

    public String getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

2.5 persistence.xml 설정

  • JPA는 persistence.xml을 사용해서 필요한 설정 정보를 관리한다.
  • META-INF/persistence.xml 클래스 패스 경로에 있으면 JPA가 자동으로 인식한다.
  • resources 밑에 META-INF 디렉토리를 생성한다. (IntelliJ 기준)
  • META-INF 디렉토리에 persistence.xml 파일을 생성한다.
  • 데이터베이스당 하나의 영속성 유닛을 등록한다.
  • 엔티티 클래스를 class 태그를 이용하여 등록한다.
  • hibernate.hbm2ddl.auto 속성을 이용하면 데이터베이스 테이블도 자동으로 생성해준다.
  • hibernate.dialect 속성은 데이터베이스 방언을 설정한다.
  • java.persistence 속성은 JPA 표준 속성으로 특정 구현체에 종속되지 않고, hibernate 속성은 하이버네이트 전용 속성이다.
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             version="2.1">
    <persistence-unit name="jpabook">
        <class>jpabook.start.Member</class>
        <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/~/jpabook"/>
            <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.user_sql_comments" value="true"/>
            <property name="hibernate.id.new_generator_mappings" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create"/>
        </properties>
    </persistence-unit>
</persistence>

2.5.1 데이터베이스 방언

  • 데이터베이스 벤더별로 제공하는 SQL 문법과 함수가 조금씩 다르다.
    • 가변문자타입 사용시 MySQL은 VARCHAR, 오라클은 VARCHAR2를 사용한다.
    • 페이징처리시 MySQL은 LIMIT, 오라클은 ROWNUM을 사용한다.
  • 이처럼 SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능을 방언이라고 한다.
  • JPA 구현체에서 이런 문제를 해결하기 위해 데이터베이스 방언 클래스를 제공한다.
  • 개발자는 JPA가 제공하는 표준 문법에 맞추어 JPA를 사용하고, 특정 데이터베이스에 의존적인 SQL은 데이터베이스 방언이 처리해준다.

2.6 애플리케이션 개발

  • 아래 애플리케이션 개발 코드는 엔티티 매니저 설정, 트랜잭션 관리, 비즈니스 로직으로 구분할 수 있다.
public class JpaMain {

    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();
    }

    private static void logic(EntityManager em) {

        String id = "id1";
        Member member = new Member();
        member.setId(id);
        member.setUsername("지한");
        member.setAge(2);

        // 등록
        em.persist(member);

        // 수정
        member.setAge(20);

        // 한 건 조회
        Member findMember = em.find(Member.class, id);
        System.out.println("findMember = " + findMember.getUsername() + ", age = " + findMember.getAge());

        // 목록 조회
        List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
        System.out.println("members.size = " + members.size());

        // 삭제
        em.remove(member);
    }
}

2.6.1 엔티티 매니저 설정

  • 엔티티 매니저 팩토리 생성
    • EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
      
    • createEntityManagerFactory 메소드의 인자로 META-INF/persistence.xml 에서 설정한 영속성 유닛의 이름을 전달한다.
    • 엔티티 매니저 팩토리는 애플리케이션 전체에서 딱 한 번만 생성하고 공유해서 사용해야 한다.
  • 엔티티 매니저 생성
    • EntityManager em = emf.createEntityManager();
      
    • JPA의 기능 대부분은 엔티티 매니저가 제공한다.
    • 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회할 수 있다.
    • 엔티티 매니저는 스레드간에 공유하거나 재사용하면 안된다.
  • 종료
    • 사용이 끝난 엔티티 매니저는 반드시 종료해야 한다.
    • em.close();
      
    • 애플리케이션을 종료할 때 엔티티 매니저 팩토리도 종료해야 한다.
    • emf.close();
      

2.6.2 트랜잭션 관리

  • JPA 사용시 항상 트랜잭션 안에서 데이터를 변경해야 한다.
  • 트랜잭션을 사용하면 비즈니스 로직이 정상 동작하면 커밋하고, 비즈니스 로직에서 예외가 발생하면 롤백한다.

2.6.3 비즈니스 로직

  • 등록
    • 등록은 persist 메소드에 저장할 엔티티 오브젝트를 전달하면 된다.
    • insert into member (id, name, age) values ('id1', '지한', 2)
      
  • 수정
    • 수정은 단순히 엔티티의 오브젝트의 값을 변경하면 된다.
    • JPA가 변경내용을 감지하여 추적한다.
    • update member set age=20, name='지한' where id='id1'
      
  • 삭제
    • 삭제는 remove 메소드에 삭제하려는 엔티티 오브젝트를 전달하면 된다.
    • delete from member where id='id1'
      
  • 한 건 조회
    • 한 건 조회는 find 메소드에 조회할 엔티티 타입과 기본 키로 매핑한 식별자 값을 전달하면 된다.
    • select * from member where id='id1'
      

2.6.4 JPQL

  • 등록, 수정, 삭제, 한 건 조회 등은 JPA를 사용하여 SQL 사용 없이 처리할 수 있다.
  • 검색 쿼리는 JPQL 이라는 쿼리 언어로 처리해야 한다.
  • JPQL은 엔티티 객체를 대상으로 쿼리한다. 즉, 클래스와 필드를 대상으로 쿼리한다.
  • SQL은 데이터베이스 테이블을 대상으로 쿼리한다.
  • List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
    
  • “select m from Member m” 이 JPQL이고 from Member는 테이블이 아니라 Member 엔티티 객체를 가리킨다.

Tags:

Categories:

Updated:

Leave a comment