JPA 사용법 알아보기

징징이·2024년 7월 2일

JPA

목록 보기
2/7

회원 클래스, 회원 테이블 매핑

  • 다음과 같은 member table, member class가 있다고 가정하자. 그렇다면 해당 클래스와 테이블을 매핑해줘야한다.
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;
}

  • Member class 자체에서 어노테이션을 통해 매핑 정보를 표시해주면 된다.

    1. @Entity
    • 이 클래스를 테이블과 매핑한다고 JPA에게 알려준다.
    • @Entity가 사용된 클래스를 엔티티 클래스라 한다.
    1. @Table
    • 엔티티 클래스에 매핑할 테이블 정보를 알려준다. 여기서는 name 속성을 사용해서 Member 엔티티를 MEMBER 테이블에 매핑했다.
    • 생략 시 클래스 이름은 테이블 이름으로 매핑된다. (더 정확히는 엔티티 이름을 사용한다.)
    1. @Id
    • 엔티티 클래스의 필드를 테이블의 기본키에 매핑한다.
    • 위 예제에서는 엔티티의 id 필드를 테이블의 ID 기본 키 컬럼에 매핑했다.
    • 이 어노테이션이 사용된 필드 : 식별자필드
    1. @Column
    • 필드를 컬럼에 매핑한다.
    • 위 예제에서는 name 속성을 사용해서 Member 엔티티의 username 필드를 MEMBER 테이블의 NAME 컬럼에 매핑했다.
  • mapping이 필요없는 필드는 그냥 작성해주면 된다.

    • age 필드에는 매핑 어노테이션이 없다. 생략하면 필드명을 사용해서 컬럼명으로 매핑한다.
    • 만약 대소문자를 구분하는 DB를 사용한다면 명시적으로 매핑해줘야한다.

    persistence.xml 설정

    • JPA는 persistence.xml을 사용해서 필요한 설정 정보 관리
    • 경로 설정 (META-INF/persistence.xml)

<?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" />

            <!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
        </properties>
    </persistence-unit>

</persistence>

분석

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
  • 설정 파일은 persistence로 시작한다.
  • XML 네임스페이스와 사용할 버전 지정
<persistence-unit name="jpabook">
  • JPA는 일반적으로 연결할 DB 당 하나의 영속성을 등록한다.
  • 영속성 유닛에는 고유한 이름을 부여해야 하는데 여기서는 jpabook이라는 이름을 사용했다.
<properties>
	<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
    ...
  • JPA 표준 속성
    • javax.persistence.jdbc.driver : JDBC 드라이버
    • javax.persistence.jdbc.user : 데이터베이스 접속 아이디
    • javax.persistence.jdbc.password : 데이터베이스 접속 비밀번호
    • javax.persistence.jdbc.url : 데이터베이스 접속 URL
  • 하이버네이트 속성
    • hibernate.dialect : 데이터베이스 방언 설정

데이터베이스 방언

  • JPA는 특정 DB에 종속적이지 않은 기술이다. 그러므로 다른 DB로 쉽게 교체가능하다.

  • DB마다 SQL문법, 함수가 조금씩 다르다는 문제점이 존재한다.
    ex

    • 데이터 타입 : mysql-varchar, oracle-varchar2
    • 다른 함수명 : SQL표준-substring(), oracle-substr()
    • 페이징 처리 : mysql-limit, oracle-rownum
  • 이러한 방언을 해결하기 위해 하이버네이트를 포함한 대부분의 JPA 구현체들은 다양한 DB 방언 클래스를 제공한다.

  • hibernate.dialect속성을 org.hibernate.dialect.H2Dialect로 설정하면 된다.

    애플리케이션 개발

package jpabook.start;

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

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(); // 엔티티 매니저 팩토리 - 종료
    }
}

코드는 크게 3부분으로 나뉘어 있다.

  1. 엔티티 매니저 설정
  2. 트랜잭션 관리
  3. 비즈니스 로직

1. 엔티티 매니저 설정

엔티티 매니저 팩토리 생성


EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");

  • JPA를 시작하기 위해서는 엔티티 매니저 팩토리를 생성해야 한다. Persistence 클래스를 사용해 JPA를 사용할 수 있게 준비한다.
  • META/INF/persistence.xml에서 이름이 jpabook인 영속성 유닛을 찾아 엔티티 매니저 팩토리를 생성한다.
  • JPA 구현체에 따라서는 데이터베이스 커넥션 풀도 생성하기 때문에 엔티티매니저팩토리 생성비용은 아주 크다. 따라서 엔티티 매니저 팩토리는 애플리케이션 전체에서 딱 한 번만 생성하고 공유해서 사용해야 한다.
엔티티 매니저 생성

EntityManager em = emf.createEntityManager();

  • 엔티티 매니저 팩토리에서 엔티티 매니저를 생성한다.
  • 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회할 수 있다. 엔티티 매니저는 내부에 데이터소스(DB 커넥션)을 유지하면서 DB와 통신한다.
  • 엔티티 매니저는 DB 커넥션과 밀접한 관계가 있으므로 스레드간에 공유하거나 재사용하면 안된다.
종료
  • 사용끝난 엔티티 매니저는 반드시 종료해야한다.
    em.close();
  • 애플리케이션 종료시 엔티티 매니저 팩토리도 종료해야한다.
    emf.close();

2. 트랜잭션 관리

  • JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 한다.
    `
    EntityTransaction tx = em.getTransaction();

        try{
            tx.begin(); // 트랙잭션 - 시작
            logic(em); //비즈니스 로직 실행
            tx.commit(); //트랜잭션 - 커밋
        } catch (Exception e){
            tx.rollback();
        }

    `

  • 트랜잭션 API를 이용해 비즈니스 로직이 정상 동작하면 트랜잭션을 커밋, 예외발생시 트랜잭션 롤백

3. 비즈니스 로직

  • 회원 엔티티를 하나 생성한 다음 엔티티 매니저를 통해 DB에 등록, 수정, 삭제, 조회한다.
public 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);
    }
  • 등록, 수정, 삭제 ,조회 작업은 엔티티매니저(em)를 통해 수행된다.
등록
String id = "id1";
Member member = new Member();
member.setId(id);
member.setUsername("지한");
member.setAge(2);

//등록
em.persist(member);
```
- 엔티티를 저장하기 위해 em의 `persist()` 메소드에 저장할 엔티티를 넘겨주면 된다.
- JPA는 회원 엔티티의 매핑정보를 분석해서 SQL을 만들어서 DB에 전달한다.

##### 수정
`
member.setAge(20);
`
- 단순히 엔티티 값만 변경했다. JPA는 어떤 엔티티가 변경됐는지 추적하는 기능을 갖추고 있다.
- 엔티티 값만 변경하면 UPDATE SQL 생성 후 DB 값을 변경한다.
##### 삭제
`
em.remove(member);
`
- 엔티티 매니저의 `remove()` 메소드에 삭제하려는 엔티티를 넘겨준다.
- JPA는 다음 DELETE SQL 생성 후 실행한다.
##### 한 건 조회
`
Member findMember = em.find(Member.class, id);
`
- `find()`메소드는 조회할 엔티티 타입과 @Id로 DB Table의 기본 key와 매핑한 식별자 값으로 엔티티 하나를 조회한다.
##### 여러건조회 (JPQL)
```java
TypedQuery<Member> query = 
em.createQuery("select m from Member m", Member.class); 
List<Member> members = query.getResultList();
```
- JPA를 사용하면 개발자는 엔티티 객체 중심으로 개발하고 DB에 대한 처리는 JPA에게 맡긴다. 등록, 수정, 삭제에서는 SQL 전혀 사용하지 않았다. 문제는 **검색쿼리**!!
- JPA는 엔티티 객체 중심으로 개바하기 때문에 검색할 때도 테이블이 아닌 엔티티 객체 대상으로 검색해야한다.
- 검색 조건이 포함된 SQL을 사용해야하기 때문에 **JQPL(java persistence query language)** 쿼리 언어를 통해 문제를 해결한다.
###### JQPL
- SQL을 추상화한 객체지향 쿼리 언어
- SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 등 사용 가능
- 차이점
  - JPQL : 엔티티 객체 대상 쿼리 (클래스와 필드 대상)
  - SQL : 데이터베이스 테이블 대상 쿼리
- `from Member`에서 `MEMBER`테이블이 아닌 회원 엔티티 객체를 말하는 것이다.
> JPQL은 데이터베이스 테이블을 전혀 알지 못한다.
- `em.createQuery(JPQL, 반환타입)`을 실행해서 쿼리 객체 생성 후 쿼리객체의 `getResultList()`메소드 호출
profile
초보 개발자의 개발 공부

0개의 댓글