[자바 ORM 표준 JPA 프로그래밍] 1주차 스터디 (2)

박서영·2026년 3월 14일

2장. JPA 시작

H2 데이터베이스 설치

CREATE TABLE MEMBER (
    ID VARCHAR(255) NOT NULL, --아이디(기본 키)
    NAME VARCHAR(255),             --이름
    AGE INTEGER NOT NULL,       --나이
    PRIMARY KEY(ID)
);

SQL 입력 후 결과


실습

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

일단 인텔리제이 IDE를 사용하고 있어서 이클립스를 따로 깔지 않고 인텔리제이에서 실습을 진행하였다.

프로젝트 구조는 아래와 같았는데, JpaMain.java가 실행 클래스이고, Member.java가 엔티티라고 한다. persistence.xml이 JPA 설정 정보가 담겨있는 파일이다.

JPA 구현체로 하이버네이트를 사용하기 위한 핵심 라이브러리는 아래가 있다고 한다.

  • hibernate-core: 하이버네이트 라이브러리
  • hibernate-entitymanager: 하이버네이트가 JPA 구현체로 동작하도록 JPA표준을 구현한 라이브러리
  • hibernate-jpa-2.1-api: JPA2.1 표준 API를 모아둔 라이브러리.

라이브러리는 메이븐을 사용해 관리한다. 메이븐은 라이브러리를 관리해주는 도구로 pom.xml에 사용할 라이브러리를 적어주면 라이브러리를 자동으로 내려받아서 관리해준다고 한다. dependencies 아래에 사용할 라이브러리들을 지정해주면 된다. groupID + artifactID + version을 적어주면 라이브러리(jar 파일)을 메이븐 공식 지정소에서 내려받아 라이브러리에 추가해준다고한다.
일전에 스프링부트 사용할 때도, 이런 .xml 파일 처럼 어떤 파일에 사용할 라이브러리들을 적으면 알아서 설치해주는 도구가 있었다. 아마 메이븐이 아니라 그레이들이었던걸로 기억한다.

객체 매핑

앞에서 만든 회원(MEMBER) 테이블을 애플리케이션(자바)에서 사용해야하므로 해당 클래스를 작성해준다. -> 예제 코드에 이미 작성이 되어있어 이걸 사용하고 코드의 매핑정보만 확인했다.

매핑 정보회원 객체회원 테이블
클래스와 테이블MemberMEMEBER
기본키idID
필드와 컬럼usernameNAME
필드와 컬럼ageAGE

여기에 각각 JPA에서 제공해주는 어노테이션을 추가한다.

@Entity
@Table(name="MEMBER")
public class Member {

@Entity: 해당 클래스를 테이블과 매핑한다고 JPA에 알려주며, 이런 클래스를 엔티티 클래스라고한다.
@Table: 엔티티 클래스에 매핑할 테이블의 정보를 알려줌. 위에서는 name 속성을 사용해 Member 엔티티를 MEMBER 테이블에 매핑하고 있다. 생략 시 클래스 이름을 테이블 이름으로 매핑한다.

@Id
    @Column(name = "ID")
    private String id;

    @Column(name = "NAME")
    private String username;

@Id: 엔티티 클래스의 필드를 테이블의 기본키에 매핑한다. 이런 필드를 식별자 필드라고 한다.
@Column: 필드를 컬럼(열)에 매핑한다. 위에서는 name 속성을 사용해 필드를 테이블의 컬럼에 매핑한다. 매핑 정보가 없는 필드의 경우 필드명을 사용해 컬럼명으로 매핑을 진행한다. 데이터베이스가 대소문자를 구분하지 않는다고 가정(구분을 위해서는 명시적인 매핑 필요)

persistence.xml 설정

JPA의 설정 정보 관리 설정 파일로 해당 파일이 META-INF/persistence.xml 클래스 패스 경로에 있으면 별도 설정 없이 JPA가 인식 가능하다고한다.

  • <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">:
    설정 파일은 persistence로 시작하며, 여기서 XML 네임스페이스와 사용할 버전을 지정한다.

  • <persistence-unit name="jpabook">:
    JPA 설정은 영속성 유닛에서부터 시작한다. 일반적으로는 연결할 데이터베이스 당 하나의 영속성 유닛을 등록한다. 영속성 유닛에는 고유한 이름을 부여해야한다.

  • JPA 표준 속성
    • jdbc.driver: JDBC 드라이버
    • jdbc.user: 데이터베이스 접속 아이디
    • jdbc.password: 데이터베이스 접속 비밀번호
    • jdbc.url: 데이터베이스 접속 url

  • 하이버네이트 속성
    - hibernate.dialect: 데이터베이스 방언 설정

javax.persistence로 시작하는 속성은 JPA 표준 속성으로 특정 구현체에 종속되지 않지만, hibernate로 시작하는 속성은 하이버네이트 전용 속성으로 하이버네이트에서만 사용 가능하다.

데이터베이스 방언(dialect)은 SQL 표준을 지키지 않고 특정 데이터베이스 만의 고유한 기능을 가지는 것을 JPA에서 이르는 말이다. 애플리케이션 개발자가 특정 데이터베이스에 종속되는 기능을 많이 쓰면 후에 교체가 어렵기에 대부분의 JPA 구현체들은 다양한 데이터베이스 방언 클래스를 제공해 이런 문제를 해결한다고한다.

애플리케이션 개발

//엔티티매니저팩토리 생성
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) {...}
  1. 엔티티 매니저 팩토리 설정
    JPA를 시작하기 위해서는 persistence.xml의 설정 정보를 사용해서 엔티티 매니저 팩토리를 생성해야한다. 이때 Persistence 클래스를 사용하는데, 이 클래스는 엔티티 매니저 팩토리를 생성해 JPA를 사용할 준비를 한다.

    META-INF/persistence.xml에서 이름이 jpabook인 영속성 유닛을 찾아 엔티티 매니저 팩토리를 생성한다고한다. 이때 persistence.xml의 설정 정보를 읽어 JPA 동작을 위한 기반 객체를 만들고 JPA 구현체에 따라 데이터베이스 커넥션 풀도 생성한다고 한다.

    엔티티 매니저 팩토리 생성 비용은 크기 때문에 애플리케이션 전체에서 딱 한 번만 생성하고 공유해 사용해야한다.

  1. 엔티티 매니저 생성
    엔티티 매니저 팩토리에서 엔티티 매니저를 생성한다. JPA의 기능 대부분은 이 엔티티 매니저가 제공한다. 대표적으로 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회할 수 있다.

    엔티티 매니저는 내부에 데이터소스(커넥션)를 유지하면서 데이터베이스와 통신한다. 엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계가 있기에 스레드간 공유하거나 재사용하면 안된다.

  2. 종료
    사용이 끝난 엔티티 매니저와 엔티티 매니저 팩토리는 종료해야한다.

트랜잭션 관리

JPA에서는 항상 트랜잭션 안에서 데이터를 변경해야한다. 트랜잭션 없이 데이터를 변경하면 예외가 발생한다. 트랜잭션을 시작하려면 엔티티 매니저에서 트랜잭션 API를 받아와야한다.

비즈니스 로직

비즈니스 로직에서는 회원 엔티티를 생성하고, 엔티티 매니저를 통해 데이터베이스에 등록, 수정, 삭제, 조회를 한다.

  1. 등록
    엔티티를 저장하기 위해서는 persist() 메소드에 저장할 엔티티를 넘겨주면 된다. JPA는 해당 클래스(엔티티)의 매핑정보를 분석해 적절한 SQL을 만들어 데이터베이스에 전달한다.

    String id = "id1";
    Member member = new Member();
    member.setId(id);
    member.setUsername("익명");
    member.setAge(2);
    
    //등록
    em.persist(member);
  2. 수정
    1장의 설명에도 나왔지만, JPA에는 수정 메소드가 따로 존재하지 않는다고 한다. 클래스(엔티티)의 필드를 수정하면 이후 트랜잭션에서 변경사항을 업데이트한다. JPA는 어떤 엔티티가 변경되었는지 추적하는 기능을 가져서 가능한 일이다.

    member.setAge(20);
  3. 삭제
    엔티티 삭제를 위해서는 remove() 메소드에 삭제하려는 엔티티를 넘겨주면 된다. 이 경우 DELETE SQL을 생성해 JPA에서 처리하게된다.

    em.remove(member);
  4. 한 건 조회
    find() 메소드는 조회할 엔티티 타입과 @Id로 데이터베이스의 테이블 기본키와 매핑한 식별자값으로 엔티티 하나를 조회하는 메소드이다.

    Member findMember = em.find(Member.class, id);

JPQL

JPA는 엔티티 객체를 중심으로 개발하기 때문에 검색 시에도 테이블이 아닌 객체 대상의 검색이 필요하다. 하지만, 이를 위해서는 데이터베이스의 모든 데이터를 애플리케이션으로 불러와 엔티티 객체로 변경한 후에 검색해야하는데 이는 현실적으로 불가능하다.

따라서 검색 조건이 포함된 SQL의 사용이 필요한데 JPA에서는 JPQL이라는 쿼리 언어를 통해 이 문제를 해결한다.

JPA는 SQL을 추상화한 JPQL이라는 객체지향 쿼리 언어를 제공한다. SQL과 문법이 거의 유사해 SELECT, JOIN, FROM, WHERE, GROUP BY, HAVING 등 사용이 가능한데 차이점이 존재한다.

  • JPQL은 엔티티 객체를 대상으로 쿼리를 한다.
  • SQL은 데이터베이스 테이블을 대상으로 쿼리를 한다.
TypedQuery<Member> query = 
em.createQuery("select m from Member m", Member.class);

List<Member> members = query.ResultList();

사용은 위의 코드와 같이할 수 있으며, JPQL은 데이터베이스 테이블은 전혀 알지 못한다. JPQL 사용을 위해서는 em.createQuery(JPQL, 반환타입) 메소드를 통해 쿼리 객체를 생성하고, 쿼리 객체의 getResultList() 메소드를 호출하면된다.


자바 ORM 표준 JPA 프로그래밍 책을 읽으며 정리하는 스터디 기록.

profile
이불 밖은 위험해.

0개의 댓글