[2장] JPA 시작
객체 매핑 시작
회원 테이블이 다음과 같이 정의되었다고 가정하겠습니다.
그리고 회원 클래스가 다음과 같이 정의되었다고 가정하겠습니다.
JPA를 사용하려면 가장 먼저 회원 클래스와 회원 테이블을 매핑해야 하고 이를 위해 회원 클래스에 JPA가 제공하는 매핑 어노테이션을 추가하겠습니다.
매핑 정보 | 회원 객체 | 회원 테이블 |
클래스와 테이블 | Member | MEMBER |
기본 키 | id | ID |
필드와 컬럼 | username | NAME |
필드와 컬럼 | age | AGE |
부연 설명
- @Entity: 클래스를 테이블과 매핑한다고 JPA가 알리며 이렇게 @Entity가 사용된 클래스를 엔티티 클래스라고 부름
- @Table: 엔티티 클래스에 매핑할 테이블 정보를 알려주며 여기서는 name 속성을 사용해서 Member 엔티티를 MEMBER 테이블에 매핑함
- @Id: 엔티티 클래스의 필드를 테이블의 기본 키에 매핑하며 이렇게 @Id가 사용된 필드를 식별자 필드라고 부름
- @Column: 필드를 컬럼에 매핑
- 매핑 정보가 없는 필드: 매핑 어노테이션을 생략하면 필드명을 사용해서 컬러명으로 매핑, 만약 대소문자를 구분하는 데이터베이스를 사용할 경우 명시적으로 @Column(name="AGE")와 같이 매핑해야 함
persistence.xml 설정
책이 지필된 시점에는 JPA가 persistence.xml을 사용해서 필요한 설정 정보를 관리했지만 현재는 SpringBoot가 제공하는 Auto-Configuration 기능과 application.yml을 활용하여 JPA, 데이터소스 및 Hibernate 등의 설정을 간편하게 처리할 수 있습니다.
따라서 책에서 소개한 persistence.xml에 정의된 설정을 기반으로 Java Config 클래스로 변환한 예제를 소개하겠습니다.
부연 설명
- 설정 파일은 persistence로 시작하며 이곳에 XML 네임스페이스와 사용할 버전을 지정
- JPA 설정은 영속성 유닛 (persistence-unit)이라는 것부터 시작하는데 일반적으로 연결할 데이터베이스 당 하나의 영속성 유닛을 등록하며 영속성 유닛에는 고유한 이름을 부여해야 하는데 여기서는 jpabook이라는 이름을 사용
- JPA 표준 속성
- javax.persistence.jdbc.driver: JDBC 드라이버
- javax.persistence.jdbc.user: 데이터베이스 접속 아이디
- javax.persistence.jdbc.password: 데이터베이스 접속 비밀번호
- javax.persistence.jdbc.url: 데이터베이스 접속 URL
- 하이버네이트 속성
- hibernate.dialect: 데이터베이스 방언 설정
1. 데이터베이스 방언
- JPA는 특정 데이터베이스에 종속적이지 않은 기술이기 때문에 다른 데이터베이스로 손쉽게 교체 가능
- 그런데 각 데이터베이스가 제공하는 SQL 문법과 함수가 조금씩 다르다는 문제점 존재
- ex) 가변 문자 타입으로 MySQL은 VARCHAR, ORACLE은 VARCHAR2를 사용
- ex) 문자열을 자르는 함수로 SQL 표준은 SUBSTRING()을 사용하지만 오라클은 SUBSTR()을 사용
- 이처럼 SQL 표준을 지키지 않거나 특정 DB만의 고유한 기능을 JPA에서는 Dialect (방언)이라고 부름
- 애플리케이션 개발자가 특정 데이터베이스에 종속되는 기능을 많이 사용하면 나중에 데이터베이스를 교체하기 어려움
- 하이버네이트를 포함한 대부분의 JPA 구현체들은 이런 문제를 해결하기 위해 다양한 데이터베이스 방언 클래스 제공
- 개발자는 JPA가 제공하는 표준 문법에 맞추어 JPA를 사용하면 되고, 특정 데이터베이스에 의존적인 SQL은 데이터베이스 방언이 처리해 주기 때문에 DB가 변경되어도 애플리케이션 코드를 변경할 필요 없이 데이터베이스 방언만 교체하면 됨
애플리케이션 개발
앞서 객체 매핑을 완료하고 persistence.xml로 JPA 설정도 완료했으므로 JPA 애플리케이션을 시작하는 코드를 작성할 차례입니다.
코드는 다음과 같이 크게 세 부분으로 나뉘어 있습니다.
- 엔티티 매니저 설정
- 트랜잭션 관리
- 비즈니스 로직
1. 엔티티 매니저 설정
- JPA를 시작하려면 우선 persistence.xml의 설정 정보를 사용해서 EntityManagerFactory를 생성해야 함
- 이떄 Persistence 클래스를 사용하는데 해당 클래스는 EntityManagerFactory를 생성해서 JPA를 사용할 수 있게 준비
- EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook"); 라인이 동작하면 META-INF/persistence.xml에서 이름이 jpabook인 persistence-unit을 찾아서 EntityManagerFactory를 생성
- JPA 구현체에 따라서는 데이터베이스 커넥션 풀도 생성하기 때문에 EntityManagerFactory를 생성하는 비용은 비싼 편
- 이에 따라 EntityManagerFactory는 빈으로 선언하여 애플리케이션 전체에서 딱 한 번만 생성하고 공유해서 사용
- 이후 EntityManagerFactory에서 JPA의 기능 대부분을 제공하는 EntityManager를 생성
- EntityManager를 사용해서 엔티티를 데이터베이스에 등록, 수정, 삭제 및 조회 가능
- EntityManager는 내부에 데이터소스를 유지하면서 DB와 통신하므로 가상의 데이터베이스로 생각할 수 있음
- EntityManager는 데이터베이스 커넥션과 밀접한 관계가 있으므로 쓰레드 간 공유하거나 재사용해서는 안됨
- 마지막으로 사용이 끝난 EntityManager는 close() 메서드를 통해 반드시 종료해야 함
- 애플리케이션을 종료할 때는 EntityManagerFactory를 close() 메서드를 통해 종료해야 함
2. 트랜잭션 관리
- JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 함
- 트랜잭션을 시작하려면 EntityManager에서 트랜잭션 API를 받아와야 함
- 트랜잭션 없이 데이터 변경 시도하면 예외 발생
3. 비즈니스 로직
- 코드에서 비즈니스 로직을 보면 등록, 수정, 삭제, 조회 작업이 EntityManager를 통해 수행되는 것을 확인 가능
- EntityManager는 객체를 저장하는 가상의 데이터베이스처럼 보임
- 등록: 엔티티를 저장하려면 EntityManager의 persist() 메서드에 저장할 에티티를 넘겨주면 되며 JPA는 회원 엔티티의 매핑 정보를 분석해서 INSERT문을 만들어 데이터베이스에 전달
- 수정: 엔티티를 수정한 후에 수정 내용을 반영하려면 em.update() 같은 메서드를 호출해야 할 것 같은데 단순히 엔티티의 값만 변경한 것을 확인 가능, JPA는 더티 체킹을 통해 어떤 엔티티가 변경되었는지 추적하는 기능을 갖추고 있어 트랜잭션 내 값을 변경할 경우 UPDATE SQL을 생성해서 데이터베이승에 값을 변경
- 삭제: 엔티티를 삭제하려면 EntityManager의 remove() 메서드에 삭제하려는 엔티티를 넘겨주면 되며 JPA는 DELETE SQL을 생성해서 실행
- 한건 조회: find() 메서드는 조회할 엔티티 타입과 @Id로 데이터베이스 테이블의 기본 키와 매핑한 식별자 값으로 엔티티 하나를 조회하는 가장 단순한 조회 메서드이며 호출 시 SELECT SQL을 생성해서 데이터베이스에 결과를 조회
4. JPQL
예제에서 하나 이상의 회원 목록을 조회하는 코드는 다음과 같습니다.
JPA.는 엔티티 객체를 중심으로 개발하므로 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색해야 합니다.
- 그런데 테이블이 아닌 엔티티 객체를 대상으로 검색하기 위해서는 DB의 모든 데이터를 애플리케이션으로 불러와서 엔티티 객체로 변경한 다음 검색해야 하는데, 이는 사실상 불가능
- JPA는 JPQL (Java Persistence Query Lanaguage)이라는 쿼리 언어로 이런 문제를 해결
- JPA는 SQL을 추상화한 JPQL이라는 객체지향 쿼리 언어를 제공
- JPQL은 엔티티 객체를 대상으로 쿼리 하는 반면 SQL은 데이터베이스 테이블을 대상으로 쿼리 하는 차이점이 존재하지만 문법 자체는 거의 유사해서 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 등을 사용 가능
- 위 코드에서 `select m from Member m`이 바로 JPQL이며 여기서 from Member는 회원 엔티티 객체를 말하는 것이지 MEMBER 테이블이 아님
- JPQL을 사용하려면 먼저 em.createQuery(JPQL, 반환타입) 메서드를 실행해서 쿼리 객체를 생성한 뒤 쿼리 객체의 getResultList() 메서드를 호출하면 됨
참고
자바 ORM 표준 JPA 프로그래밍 - 김영한 저