해당 내용은 인프런 김영한 강사님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편'의 강의를 기반으로 작성했습니다.
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard
내가 JPA 메소드를 쓰기는 하지만, 정확하게 알고 쓰는 게 아니라서, 객체 지향 프로그래밍과 백엔드 개발을 위해서는 필수 소양이기 때문에 이번에 제대로(?!) 공부하고 넘어가보려 한다.
인프런의 김영한 강사님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편' - '실전! 스프링 데이터 JPA' - '실전! Querydsl'까지가 목표!
그날그날 내가 공부한 내용 정리하는 TIL!!
Java Persistence API
자바에서 데이터베이스와의 상호작용을 위한 표준 인터페이스
객체지향 프로그래밍과 관계형 데이터베이스 간의 간격을 줄여주는 ORM(Object-Relational Mapping) 프레임워크 중 하나로, 프로그래밍에서 쓰이는 객체(Object)와 데이터베이스 테이블(Relational DB)을 매핑
데이터베이스 테이블과 연결되는 자바 클래스
EntityManager를 생성하는 객체
데이터베이스와의 연결을 관리하고, 데이터를 영구적으로 저장하는 엔티티 매니저를 생성하는 공장(Factory) 역할
생성하는 데에 많은 자원을 사용하여 애플리케이션당 하나의 EntityManagerFactory만 생성
-> 여러 스레드에서 동시에 사용 가능
데이터베이스와의 모든 상호작용을 관리하는 인터페이스
엔티티를 데이터베이스에 저장, 조회, 삭제할 때 사용
데이터베이스 커넥션 하나당 하나의 Entity Manager 사용
-> 각 스레드마다 별도의 인스턴스를 사용
객체 지향적으로 데이터를 쿼리하기 위한 언어
SQL과 유사하나, 데이터베이스가 아닌 자바 객체를 대상으로 쿼리
그래서, 어떤 DBMS든 그에 맞게 번역(?) 가능
SQL을 직접 작성하지 않고도 객체의 메서드를 호출하여 데이터베이스와 상호작용
-> 개발자가 일일이 쿼리를 작성할 필요가 없어서 생산성 증가
SQL을 객체화하여 자바 엔티티를 통해 데이터 처리 가능
즉, 테이블 대신 객체로 작업이 가능
데이터베이스 스키마의 변경이 발생할 때, JPA를 사용하면 데이터베이스 테이블의 직접적인 변경 없이 자바의 엔티티 클래스만 수정하여 쉽게 대응
데이터베이스마다 다를 수 있는 SQL 쿼리의 작성 부담 감소
-> JPA는 데이터베이스에 독립적인 방식으로 쿼리를 생성하므로, 데이터베이스를 교체할 때 수정해야 할 코드의 양 감소
객체들 간의 관계(예: 일대다, 다대일)를 쉽게 매핑하고 관리
-> 객체 간의 연관 관계를 데이터베이스 테이블의 외래키로 매핑할 때 발생하는 복잡성 감소
JPA는 영속성 컨텍스트를 통해 데이터베이스에 대한 접근 최적화
-> 이를 통해 데이터의 캐싱, 중복 쿼리 방지 등을 통해 데이터 접근 성능 향상
아직은 잘 모르겠지만... 알아가보자.
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: ㅇㅇ 번역할게
가 된다는 것!
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 데이터베이스가 정상적으로 초기화
직접 데이터베이스 테이블 만들어주고...
위 테이블과 매핑이 되는 엔티티를 만듦
위에서 말했듯이EntityManagerFactory는 로딩 시점에 하나만 생성
실제 DB에 저장하거나 조회하는 트랜잭션 단위마다 엔티티 매니저를 생성해야 함
Member객체를 만들고(값도 Set)
em.persist(member)를 통해 해당 객체를 저장
그리고 위 2번 작업 전후에 트랜잭션을 열어주고 commit해야(혹은 rollback) 함
왜? 모든 데이터를 변경하는 작업은 트랜잭션 안에서 이루어져야
-> 데이터베이스 커넥션을 받는다고 생각하고 트랜잭션을 얻어서 시작
쿼리를 만든 적도 없는데 쿼리가 나감
(위처럼, 쿼리가 예쁘게 나가고, 출력이 되고, 주석이 달리는 건 persistence.xml에 쓰인 설정 때문)
바로 JPA가 맵핑 정보를 보고 넣어줬기 때문
추후, 테이블 이름과 필드 이름도 엔티티 클래스에 지정 가능
트랜잭션 작업 중 문제가 생겼거나 예외가 터지면, 그 이후 실행이 안 되는 일을 방지하기 위해
(가령, 위 코드에서 commit이 안 되면, close 함수들이 실행이 안 될 것임)
따라서 트랜잭션을 try-catch 안에 넣어서
정상적으로 작동하면 commit
뭔가 예외가 발생하면 catch안의 rollback
그리고 위 모든 작업이 끝나면 엔티티 매니저 close
(왜? 엔티티 매니저가 데이터베이스 커넥션을 물고 동작하기 때문에)
엔티티 매니저의 find를 통해 객체를 반환
find를 통해 아이디가 1인 데이터를 객체로 반환 받아 해당 객체의 name을 "helloJPA"로 변경
em.persist도 없지만
자바 컬렉션을 다루는 것처럼 다루도록 설계되어 있어서 자바 객체에서 값만 바꿔도 테이블의 값이 변경
JPA를 통해 엔티티를 가져오면 그 객체는 JPA 관리함
-> 변경이 됐는지 안 됐는지 트랜잭션을 커밋하는 시점에 체크
그래서 바뀌었으면 업데이트 쿼리를 만들어서 날리고, 트랜잭션이 커밋이 됨
JPA가 아무리 날고 기어도 RDBMS는 결국 트랜잭션 안에서 데이터 변경
코드 상에서 트랜잭션을 관리하지 않아도, DB가 트랜잭션이라는 개념을 쿼리가 올 때마다 내부적으로 트랜잭션 단위로 처리하긴 함
조건을 달아서 조회하고 싶다면?
테이블은 수백 개가 넘을 수 있고, 필요하면 조인도 하고, 통계도 조회해야 하고...
그러면 어떻게 하지?
-> JPA에서는 JPQL로 도와줌
위처럼 쿼리를 날릴 수 있음
그러나 테이블이 아닌 Member 객체를 대상으로 쿼리
애플리케이션의 코딩만 했는데 JPA가 여러 가지 각 DBMS에 맞게 번역해서 각기 다른 쿼리를 날림
JPA가 있으면 엔티티 객체를 중심으로 개발 가능
그런데 조건이 필요한 검색 쿼리는 어떻게 하지? 이때도 객체를 가져와야 함
❗️ 모든 데이터를 객체로 변환해서 검색하는 건 불가능
-> 검색 조건이 포함된 SQL을 날려야 한다.
어떻게?
실제 RDB에 있는 물리적인 테이블을 대상으로 쿼리를 날리면 그 DB에 종속이 되므로,
테이블이 아닌 엔티티 객체를 대상으로 쿼리를 할 수 있는 JPQL이라는 게 제공
따라서 JPQL은 한마디로 정의하면 객체 지향 SQL