JPA 공부하기 - 시작하기

ANN·2024년 12월 4일

JPA(TIL)

목록 보기
1/4
post-thumbnail

해당 내용은 인프런 김영한 강사님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편'의 강의를 기반으로 작성했습니다.

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

📢 JPA를 공부하게 된 이유


내가 JPA 메소드를 쓰기는 하지만, 정확하게 알고 쓰는 게 아니라서, 객체 지향 프로그래밍과 백엔드 개발을 위해서는 필수 소양이기 때문에 이번에 제대로(?!) 공부하고 넘어가보려 한다.

인프런의 김영한 강사님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편' - '실전! 스프링 데이터 JPA' - '실전! Querydsl'까지가 목표!

그날그날 내가 공부한 내용 정리하는 TIL!!

📢 JPA란?


Java Persistence API

자바에서 데이터베이스와의 상호작용을 위한 표준 인터페이스
객체지향 프로그래밍과 관계형 데이터베이스 간의 간격을 줄여주는 ORM(Object-Relational Mapping) 프레임워크 중 하나로, 프로그래밍에서 쓰이는 객체(Object)와 데이터베이스 테이블(Relational DB)을 매핑

JPA의 핵심 개념

Entity

데이터베이스 테이블과 연결되는 자바 클래스

Entity Manager Factory

EntityManager를 생성하는 객체

데이터베이스와의 연결을 관리하고, 데이터를 영구적으로 저장하는 엔티티 매니저를 생성하는 공장(Factory) 역할
생성하는 데에 많은 자원을 사용하여 애플리케이션당 하나의 EntityManagerFactory만 생성
-> 여러 스레드에서 동시에 사용 가능

Entity Manager

데이터베이스와의 모든 상호작용을 관리하는 인터페이스

엔티티를 데이터베이스에 저장, 조회, 삭제할 때 사용
데이터베이스 커넥션 하나당 하나의 Entity Manager 사용
-> 각 스레드마다 별도의 인스턴스를 사용

JPQL

객체 지향적으로 데이터를 쿼리하기 위한 언어
SQL과 유사하나, 데이터베이스가 아닌 자바 객체를 대상으로 쿼리
그래서, 어떤 DBMS든 그에 맞게 번역(?) 가능

JPA의 장점

1. 생산성 증가

SQL을 직접 작성하지 않고도 객체의 메서드를 호출하여 데이터베이스와 상호작용
-> 개발자가 일일이 쿼리를 작성할 필요가 없어서 생산성 증가

2. 객체지향적인 코드 작성

SQL을 객체화하여 자바 엔티티를 통해 데이터 처리 가능
즉, 테이블 대신 객체로 작업이 가능

3. 변경에 유연한 설계

데이터베이스 스키마의 변경이 발생할 때, JPA를 사용하면 데이터베이스 테이블의 직접적인 변경 없이 자바의 엔티티 클래스만 수정하여 쉽게 대응
데이터베이스마다 다를 수 있는 SQL 쿼리의 작성 부담 감소
-> JPA는 데이터베이스에 독립적인 방식으로 쿼리를 생성하므로, 데이터베이스를 교체할 때 수정해야 할 코드의 양 감소

4. 관계형 데이터베이스와 객체 간의 매핑을 쉽게 처리

객체들 간의 관계(예: 일대다, 다대일)를 쉽게 매핑하고 관리
-> 객체 간의 연관 관계를 데이터베이스 테이블의 외래키로 매핑할 때 발생하는 복잡성 감소

5. 캐싱과 최적화

JPA는 영속성 컨텍스트를 통해 데이터베이스에 대한 접근 최적화
-> 이를 통해 데이터의 캐싱, 중복 쿼리 방지 등을 통해 데이터 접근 성능 향상

아직은 잘 모르겠지만... 알아가보자.

JPA와 Hibernate

JPA는 자바에서 ORM을 위한 표준 인터페이스
Hibernate는 JPA의 구현체 중 하나
(원래는 Hibernate 오픈소스가 먼저였다던데...)

📢 프로젝트 생성


데이터베이스 : H2 2.3.232
Java 언어 : 21
프로젝트 : Maven
JPA 하이버네이트 : 6.4.2.Final
JPA API : 3.2.0

위 버전에 맞게 pom.xml생성

이 정도야...(?)
사실 Maven이 익숙하진 않지만 환경 설정은 비슷해서 이해가 가능했음

여기서 다시 알게 된 사실 하나는,
hibernate 라이브러리인 Dialect는 다양한 DBMS를 사용하기 위해 사용된다는 것

MySQL, Oracle, H2 등의 DBMS는 표준적이지 않은 것들이 존재
-> 각각의 DBMS가 제공하는 문법이나 함수가 조금씩 다른데, 해당하는 Dialect를 쓰면
JPA: ㅇㅇ 번역할게
가 된다는 것!

📢 프로젝트 실습


❓ 회원 데이터 생성

1. 기본 동작

persistenceUnitName : persistence.xml에서 설정한 이름
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
: 엔티티 매니저 팩토리 생성
EntityManager em = emf.createEntityManager();
: 엔티티 매니저 팩토리로부터 엔티티 매니저 생성
그리고 위 객체들은 모두 종료

12월 04, 2024 5:42:08 오후 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [name: hello]
12월 04, 2024 5:42:08 오후 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.4.2.Final
12월 04, 2024 5:42:08 오후 org.hibernate.cache.internal.RegionFactoryInitiator initiateService
INFO: HHH000026: Second-level cache disabled
12월 04, 2024 5:42:08 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
12월 04, 2024 5:42:08 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.h2.Driver
12월 04, 2024 5:42:08 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:h2:~/test]
12월 04, 2024 5:42:08 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=sa}
12월 04, 2024 5:42:08 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
12월 04, 2024 5:42:08 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
12월 04, 2024 5:42:09 오후 org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl constructDialect
WARN: HHH90000025: H2Dialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
12월 04, 2024 5:42:09 오후 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
12월 04, 2024 5:42:09 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PoolState stop
INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:~/test]

Hibernate와 H2 데이터베이스가 정상적으로 초기화

2. 매핑

직접 데이터베이스 테이블 만들어주고... 위 테이블과 매핑이 되는 엔티티를 만듦
  1. 위에서 말했듯이EntityManagerFactory는 로딩 시점에 하나만 생성
    실제 DB에 저장하거나 조회하는 트랜잭션 단위마다 엔티티 매니저를 생성해야 함

  2. Member객체를 만들고(값도 Set)
    em.persist(member)를 통해 해당 객체를 저장

  3. 그리고 위 2번 작업 전후에 트랜잭션을 열어주고 commit해야(혹은 rollback) 함
    왜? 모든 데이터를 변경하는 작업은 트랜잭션 안에서 이루어져야
    -> 데이터베이스 커넥션을 받는다고 생각하고 트랜잭션을 얻어서 시작

3. 결과

쿼리를 만든 적도 없는데 쿼리가 나감
(위처럼, 쿼리가 예쁘게 나가고, 출력이 되고, 주석이 달리는 건 persistence.xml에 쓰인 설정 때문)

바로 JPA가 맵핑 정보를 보고 넣어줬기 때문
추후, 테이블 이름과 필드 이름도 엔티티 클래스에 지정 가능

4. ETC...

트랜잭션 작업 중 문제가 생겼거나 예외가 터지면, 그 이후 실행이 안 되는 일을 방지하기 위해
(가령, 위 코드에서 commit이 안 되면, close 함수들이 실행이 안 될 것임)

따라서 트랜잭션을 try-catch 안에 넣어서
정상적으로 작동하면 commit
뭔가 예외가 발생하면 catch안의 rollback
그리고 위 모든 작업이 끝나면 엔티티 매니저 close
(왜? 엔티티 매니저가 데이터베이스 커넥션을 물고 동작하기 때문에)

❓ 회원 데이터 조회

엔티티 매니저의 find를 통해 객체를 반환

  • 첫 번째 파라미터 : 엔티티 클래스

❓ 회원 데이터 수정

find를 통해 아이디가 1인 데이터를 객체로 반환 받아 해당 객체의 name"helloJPA"로 변경

em.persist도 없지만
자바 컬렉션을 다루는 것처럼 다루도록 설계되어 있어서 자바 객체에서 값만 바꿔도 테이블의 값이 변경
JPA를 통해 엔티티를 가져오면 그 객체는 JPA 관리함
-> 변경이 됐는지 안 됐는지 트랜잭션을 커밋하는 시점에 체크
그래서 바뀌었으면 업데이트 쿼리를 만들어서 날리고, 트랜잭션이 커밋이 됨

❗️ 정리 및 주의

  • 엔티티 매니저 팩토리라는 건 웹 서버가 올라오는 시점에, 즉 DB당 하나만 생성
  • 엔티티 매니저는 고객의 요청이 올 때마다 썼다가 버렸다가, -> 1회성
    • 여러 스레드에서 공유하는 경우 트랜잭션이 충돌하거나 의도하지 않은 데이터 손실이 발생하고, 동시성 이슈가 발생
  • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행

    JPA가 아무리 날고 기어도 RDBMS는 결국 트랜잭션 안에서 데이터 변경
    코드 상에서 트랜잭션을 관리하지 않아도, DB가 트랜잭션이라는 개념을 쿼리가 올 때마다 내부적으로 트랜잭션 단위로 처리하긴 함

📢 JPQL

조건을 달아서 조회하고 싶다면?
테이블은 수백 개가 넘을 수 있고, 필요하면 조인도 하고, 통계도 조회해야 하고...
그러면 어떻게 하지?
-> JPA에서는 JPQL로 도와줌

위처럼 쿼리를 날릴 수 있음
그러나 테이블이 아닌 Member 객체를 대상으로 쿼리

애플리케이션의 코딩만 했는데 JPA가 여러 가지 각 DBMS에 맞게 번역해서 각기 다른 쿼리를 날림

❗️ 정리

JPA가 있으면 엔티티 객체를 중심으로 개발 가능
그런데 조건이 필요한 검색 쿼리는 어떻게 하지? 이때도 객체를 가져와야 함

❗️ 모든 데이터를 객체로 변환해서 검색하는 건 불가능
-> 검색 조건이 포함된 SQL을 날려야 한다.
어떻게?
실제 RDB에 있는 물리적인 테이블을 대상으로 쿼리를 날리면 그 DB에 종속이 되므로,
테이블이 아닌 엔티티 객체를 대상으로 쿼리를 할 수 있는 JPQL이라는 게 제공

따라서 JPQL은 한마디로 정의하면 객체 지향 SQL

0개의 댓글