왜?
sql, jpql, mybatis 등등을 활용할때는 개발자가 결국 직접 sql을 쓰는것과같다.
하지만 JPA는 객체지향으로 쿼리를 만들고 개발할수있다.
출처
목표
객체와 테이블 설계 매핑
- 기본 키와 외래키
- 1:N, N:1, 1:1, N:M 매핑
- 실무 노하우 + 성능까지 고려
- 복잡한 시스템도 JPA로 설계 가능
JPA 내부 동작 방식 이해
- JPA의 내부 동작 방식을 이해하지 못함
- JPA가 어떤 SQL을 만들어 내는지 이해
- JPA가 언제 SQL을 실행하는지 이해
JPA 기본
SQL 단점
- 객체에 필드 추가시 insert update delete 문에 모두 다 추가해야한다.
- 객체 지향 프로그래밍의 추상화, 캡슐과, 다형성, 상속등과 지향점이 틀리다
객체(JAVA)와 관계형 데이터 데이터베이스의 차이(지향점차이)
상속
관계형 DB에서는 상속에서 추가, 조회할려면 각각 모든 해당 DB 모두 join해야한다.연관관계
member.getTeam()
VSJOIN ON M.TEAM_ID = T.TEAM_ID
객체는 team_id가 아닌 team객체의 주소를 가짐또한 RDB에서는 SQL문에서 이미 가져올수있는 값이 정해진다.
member.getTeam()
는 되지만,member.getOrder().getDelivery()
는 안될수있음(신뢰성문제)데이터 타입
데이터 식별 방법
==
를 할경우? SQL은 결국 마지막에 new 객체이므로 참조값 다를수밖에없다
객체를 자바 컬렉션에 저장 하듯, DB에 저장가능할까? -> JPA
신뢰할수있는 엔티티, 계층을 가진다!
(객체가있다면 어떤 필드를 get()해도 null은 일어나지않음)
JPA 란?
ORM이란?
- 객체 관계 매핑(Object-relational mapping)
- 객체는 객체대로 설계, 관계형 데이터베이스는 관계형 데이터베이스로 설계
- ORM 프레임워크가 중간에서 매핑
- 기본구조
- Save()
- Find()
JPA를 사용해야하는이유
- SQL 중심적인 개발에서 객체 중심으로 개발
- 생산성
- 유지보수
- 패러다임의 불일치 해결
- 성능
- 데이터 접근 추상화와 벤더 독립성
- 표준
1 | jpa.persist(member) //저장 |
JPA의 성능 최적화 기능
- 1차 캐시와 동일성(identity) 보장 => 캐시
- 트랜잭션을 지원하는 쓰기 지연(transactional write-behind) => 모아서보냄
- 지연 로딩(Lazy Loading) => 미루다가, 객체의 데이터가 필요한 시점에 쿼리, 즉시로딩을 선택한다면 join으로가져옴
JPA 구동방식
JPA 쓸때 주의해야 할점
- 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유 (싱글톤)
- 엔티티 매니저는 쓰레드간에 공유X (사용하고 버려야 한다). (트랜잭션 단위로 엔티티 매니저 쓴다!)
- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행 (모든 RDB내부에서도 트랜잭션 단위로 일어남, JPA도 동일하게 설계됨)(ex. 더티체킹)
JPQL 소개
- JPA를 사용하면 엔티티 객체를 중심으로 개발
- 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
- 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검 색 조건이 포함된 SQL이 필요
- JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공 (dialect만 바꾸면 모든 DB에 맞춰 SQL번역됨)
- SQL과 문법 유사, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
- JPQL은 엔티티 객체를 대상으로 쿼리
- SQL은 데이터베이스 테이블을 대상으로 쿼리
JPA 설정 예시
dependency
1
2
3
4
5
6
7
8
9
10
11<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.3.10.Final</version>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>src/main/resources/META-INF/persistence.xml
- jdbc driver, 및 DB 설정
persistence-unit name="hello"
이름은 추후EntityManagerFactory
를 생성시 name으로 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- 옵션 -->
<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" />-->
</properties>
</persistence-unit>
</persistence>EntityManagerFactory
생성 -> Spring에서 단 한개만 존재 싱글톤EntityManager
생성 -> 트랜잭선 단위로 생선한다고 생각예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // 한번만 소환
EntityManager em = emf.createEntityManager(); // 한트랜잭션 당 하나만 만들기
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// Member member = em.find(Member.class, 1L);
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.setFirstResult(2)
.setMaxResults(5)
.getResultList();
for (Member member:result){
System.out.println("member = " + member);
}
tx.commit();
}
catch (Exception e){
tx.rollback();
} finally {
em.close();
}
emf.close();