JPA 구동 방식
JPA는 Persistence라는 클래스가 있다
여기서 설정 정보를 조회해서 EntityManagerFactory라는 클래스를 만든다.
EntityManagerFactory에서 EntityManager을 생성해서 사용한다.
객체와 테이블을 생성하고 매핑하기
테이블 생성
create table Member (
id bigint not null,
name varchar(255),
primary key (id)
);
테이블에 매핑되는 객체 생성
@Entity
public class Member {
@Id
private Long id;
private String name;
//Getter, Setter …
}
- @Entity: JPA가 관리할 객체
- 처음 로딩될때 @Entity를 통해 JPA가 인식하고 관리할 수 있다.
- @Id: 데이터베이스 PK와 매핑
실습 - 회원 관리
회원 등록
JpaMain
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
...
em.close();
emf.close();
}
}
EntityManagerFactory는 애플리케이션 로딩 시점에 하나만 만들어 놔야 한다.
그리고 DB 커넥션을 얻어서 쿼리를 보내고 종료되는 그런 일관적인 단위를 할 때마다 EntityManager를 만들어줘야 한다. EntityManager를 만들었다는 건 쉽게 생각해서 데이터베이스 커넥션을 하나 받았다고 생각하면 된다.
EntityManager는 쓰레드간에 절대 공유하면 안 된다.(사용하고 버려야 한다).
EntityManager em = emf.createEntityManager();
Member member = new Member();
member.setId(1L);
member.setName("HelloA");
em.persist(member);
위와 같이 코드를 작성하고 실행을 하면 돌아가지 않는다.
JPA에서는 트랜잭션 단위가 중요하다.
JPA에서 데이터를 변경하는 모든 작업은 꼭 트랜잭션 안에서 작업을 해야 된다.
RDB는 데이터 변경 자체를 트랜잭션 안에서 실행하도록 설계되어 있다.
EntityTransaction tx = em.getTransaction();
tx.begin();
Member member = new Member();
member.setId(1L);
member.setName("HelloA");
em.persist(member);
tx.commit();
그래서 em.getTransaction(); 을 통해 EntityTransaction을 받아와 트랜잭션을 시작하고 종료해야 한다.
트랜잭션 시작과 종료 사이에 데이터를 변경하는 작업을 수행하면 된다.
실행결과
Hibernate:
/* insert for jpa_basic.ex1_hello_jpa.hellojpa.Member */
insert into
Member (name, id)
values
(?, ?)
참고 - 옵션 설명
<!-- 옵션 -->
<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.hbm2ddl.auto" value="create" />
- <property name="hibernate.show_sql" value="true"/>
- 콘솔에 쿼리가 출력돼서 볼 수 있다.
- <property name="hibernate.format_sql" value="true"/>
- 쿼리를 이쁘게 포맷팅 해준다.
- <property name="hibernate.use_sql_comments" value="true"/>
- 실행 결과에서 /* insert for jpa_basic.ex1_hello_jpa.hellojpa.Member */ 부분에 해당한다.
- 쿼리가 왜 나온 건지 알려주는 주석이다.
- <property name="hibernate.hbm2ddl.auto" value="create" />
- 테이블을 자동으로 만들어준다. create, update, none 등 여러 옵션이 있다.
try - catch 문 추가
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = new Member();
member.setId(2L);
member.setName("HelloB");
em.persist(member);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
회원 수정
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member findMember = em.find(Member.class, 1L);
findMember.setName("HelloJPA");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
위 코드를 보면 회원 수정 시 findMember.setName("HelloJPA"); 와 같이 변경만 하고 끝이다.
따로 persist()를 하지 않아도 된다. 마치 자바 컬렉션의 데이터를 변경하는 것과 같이 생각하면 된다.
실행결과
Hibernate:
select
m1_0.id,
m1_0.name
from
Member m1_0
where
m1_0.id=?
Hibernate:
/* update
for jpa_basic.ex1_hello_jpa.hellojpa.Member */
update
Member
set
name=?
where
id=?
실행결과를 보면 업데이트 쿼리가 날아간 걸 볼 수 있다.
어떻게 자바 객체의 값만 바꿨는데 업데이트 쿼리가 나간 걸까?
JPA를 통해서 엔티티를 가져오면 JPA가 해당 엔티티를 관리한다.
그리고 JPA가 변경이 됐는지 안 됐는지 트랜잭션을 커밋하는 시점에 다 체크한다.
그래서 변경된 걸 감지하면 트랜잭션 커밋하기 직전에 업데이트 쿼리를 만들어서 날리고 트랜잭션이 커밋된다.
JPQL 소개
가장 단순한 조회 방법은 EntityManager.find()와 같이 조회하면 된다.
하지만 나이가 18살 이상인 회원을 모두 검색하고 싶다면? 와 같이 특정 조건을 추가해서 조회를 하고 싶다면 JPQL을 사용해야 한다.
현업 개발 고민
테이블이 정말 많다. 수십 개부터 수백 개가 될 텐데 필요하면 조인도 해야 된다.
이런 데이터들에서 원하는 데이터들을 최적화해서 가져와야 되고 필요하면 통계성 쿼리도 날리고 해야 된다.
이걸 JPA에서 JPQL이란 걸로 도와준다.
JPQL 코드 예시
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.getResultList();
JPA 입장에서는 위 JPQL 코드를 짤 때 테이블을 대상으로 절대 코드를 짜지 않는다.
멤버 객체를 대상으로 쿼리를 짠다고 보면 된다.
그래서 대상이 테이블이 아니고 객체가 대상이 된다.
실행결과
Hibernate:
/* select m from Member as m */
select
m1_0.id,
m1_0.name
from
Member m1_0
주석에는 위에서 작성한 JPQL문이 있다.
SQL문을 보면 select 하고 필드를 다 나열했다.
근데 작성한 JPQL을 보면 멤버 엔티티를 선택한 거라고 보면 된다.
JPQL과 SQL 간에 살짝 미묘한 차이가 존재한다.
페이징
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getResultList();
setFirstResult(1)와 setmaxResults(10)는 1번부터 10개 가져와 라는 의미다.
실행결과
Hibernate:
/* select m from Member as m */
select
m1_0.id,
m1_0.name
from
Member m1_0
offset
? rows
fetch
first ? rows only
쿼리에 limit offset이 자동으로 반영된다.
만약에 H2가 아니라 오라클로 변경하다면 위 JPQL을 그대로 쓸 수 있다.
JPQL이라는 게 객체를 대상으로 하는 객체지향 쿼리라고 보면 된다.
JPQL을 짜 놓으면 방언에 맞춰서 여러 가지 들을 각 DB에 맞게 번역을 해준다.
JPA를 사용하면 엔티티 객체를 중심으로 개발한다.
문제는 검색 쿼리이다. 현업에서는 조인을 엄청 많이 쓴다.
문제는 데이터를 단건을 가져오는 게 아니라 검색을 해야 된다.
즉 데이터베이스에 데이터를 최대한 필터링해서 가져와야 된다.
하지만 테이블에서 데이터를 가져오면 JPA의 사상이 깨진다.
그래서 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 쿼리를 짤 수 있는 문법이 들어가 있다.
모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능하기 때문이다.
애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요하다.
근데 그 SQL을 실제 RDB에 있는 물리적인 테이블을 대상으로 쿼리를 날려버리면 그 DB에 종속적으로 설계가 되어버린다. 그래서 그게 아니라 엔티티 객체를 대상으로 쿼리를 할 수 있는 JPQl 이라는게 제공된다.
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공한다.
기본적으로 SQL과 문법과 유사하다.
SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원한다.
차이는 JPQL은 엔티티 객체를 대상으로 한 쿼리이고 SQL은 데이터베이스 테이블을 대상으로 한 쿼리이다.
이를 통해 얻을 수 있는 이점은 DB를 바꿔도 JPQL 자체를 변경할 필요가 없다.
정리
테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리
SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
JPQL을 한마디로 정의하면 객체 지향 SQL이다.
JPQL을 실행하면 방언과 합쳐져서 현재 데이터베이스에 맞는 적절한 SQL이 나간다.
엔티티와 JPQL 관련 질문 글
JPQL은 Java Persistence Query Language의 약자로, JPA(Java Persistence API)를 사용하여 작성되는 객체지향 쿼리 언어입니다. JPQL은 엔티티 객체를 대상으로 쿼리를 수행하지만, 최종적으로는 SQL로 변환되어 데이터베이스에 적용됩니다.
JPQL이 엔티티 객체를 대상으로 쿼리를 한다는 의미는, JPQL이 데이터베이스의 테이블 구조와 직접적으로 대응하는 SQL과 달리, 애플리케이션의 엔티티 클래스와 그 속성을 기반으로 쿼리를 작성한다는 것을 의미합니다.
이 점에서 JPQL은 데이터베이스 구조가 아니라 애플리케이션 도메인 모델에 집중하기 때문에 보다 객체지향적인 접근을 가능하게 합니다.
엔티티 객체에 대한 질문이 있는데, 엔티티(Entity)는 JPA에서 관리하는 데이터의 기본 단위로, 보통 데이터베이스의 테이블에 해당하는 데이터 구조를 자바 클래스로 매핑할 때 사용됩니다. @Entity 어노테이션이 붙은 클래스는 데이터베이스의 테이블과 연결될 클래스임을 의미하며, 이런 클래스의 인스턴스를 엔티티 객체라고 합니다. 즉, 엔티티 객체는 데이터베이스 내의 데이터를 자바 애플리케이션에서 사용할 수 있도록 자바 객체의 형태로 표현한 것입니다. 여기서 ‘한 번이라도 영속화를 시켜준 실체’라고 말씀하셨는데, 이는 엔티티 매니저에 의해 관리되는 상태, 즉 JPA 컨텍스트 내에 저장되어 있는 상태를 의미합니다
참고
김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본 편 강의를 보고 정리한 내용입니다.
'JPA > JPA 기본' 카테고리의 다른 글
6. 다양한 연관관계 매핑 (0) | 2024.08.10 |
---|---|
5. 연관관계 매핑 기초 (0) | 2024.08.10 |
4. 엔티티 매핑 (0) | 2024.08.09 |
3. 영속성 관리 - 내부 동작 방식 (0) | 2024.08.08 |
1. JPA 소개 (0) | 2024.08.08 |