김영환님의 강의 자바 ORM 표준 JPA 프로그래밍 - 기본편 보면서 공부한 내용입니다.
📝 JPA란?
💡 ORM이란?
- Object-relational mapping(객체 관계 매핑)
- 객체는 객체대로 설계
- 관계형 데이터베이스는 관계형 데이터베이스대로 설계
- ORM 프레임워크가 중간에서 매핑
→ 패러다임 불일치 해결- 대중적인 언어에는 대부분 ORM 기술이 존재
📝 JPA는 애플리케이션과 JDBC 사이에서 동작
JDBC API
를 사용하는데 개발자가 직접 사용했다면, JPA는 개발자 대신 사용한다.JPA동작 - 저장
JPA동작 - 조회
JPA는 인터페이스의 모음 (하이버네이트, EclipseLink, DataNucleus)
📝 JPA를 왜 사용해야 하는가?
✅ 조회시 JOIN도 알아서 해준다
연관관계, 객체 그래프 탐색
신뢰할 수 있는 엔티티, 계층
✅ 지연로딩(Lazy Loading)이라는 기술로 가능하다
비교하기
1차 캐시와 동일성 보장
✅ 첫번째 조회시 sql 쿼리로 조회한 후에 두번째 조회시 jpa가 가지고있는 메모리상(캐시)에서 반환한다
쓰기 지연
지연 로딩과 즉시 로딩
✅ 지연 로딩 : 객체가 실제 사용될 때 로딩
(ex : member만 먼저 조회하고 team을 후에 사용할 때)
✅ 즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
(ex : member와 team을 조회할 때 거의 같이 조회할 때)
📝 프로젝트 생성
💡 H2 데이터베이스 설치와 실행
- JPA는 데이터베이스 기반 개발을 해야 하기 때문에 h2database를 사용
- 최고의 실습용 DB
- 가볍다.(1.5M)
- 웹용 쿼리툴 제공
- MySQL, Oracle 데이터베이스 시뮬레이션 기능
- 시퀀스, AUTO INCREMENT 기능 지원
💡 메이븐
- http://maven.apache.org/
- 자바 라이브러리, 빌드 관리
- 라이브러리 자동 다운로드 및 의존성 관리
- 최근에는 그래들(Gradle)이 점점 유명
SELECT H2VERSION() FROM DUAL;
를 입력하면 설치한 버전을 확인할 수 있다 <dependencies>
<!-- JPA 하이버네이트 -->
<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.200</version>
</dependency>
</dependencies>
hibernate
로 써진 설정을 모두 바꿔야함(즉, hibernate 전용 옵션)<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>
📝 데이터베이스 방언
JPA는 특정 데이터베이스에 종속 X
각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다름
방언: SQL 표준을 지키지 않는 특정 데이터베이스만의 고유한 기능
📝 JPA 구동방식
💡 주의
- 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유
- 엔티티 매니저는 쓰레드간에 공유X (사용하고 버려야 한다).
- 왜? 엔티티 매니저는 고객의 요청이 올 때 마다 계속 사용 및 완료를 실행하기 때문- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
Module ex1-hello-jpa SDK 17 does not support source version 1.5.
java Compiler을 1.5에서 8로 수정하여 해결
참고 : https://zion830.tistory.com/114
'org.h2.jdbc.JdbcSQLNonTransientConnectionException: Connection is broken: "java.net.SocketException: 현재 연결은 사용자의 호스트 시스템의 소프트웨어의 의해 중단되었습니다:'
h2에서 url을 pom.xml과 동일한 버전으로 맞춘뒤 실행하여 해결
exception in thread "main" java.lang.noclassdeffounderror:
persistence.xml의 javax부분을 jakarta로 수정하여 해결
💡 도와주신 천사님 왈 :
최신 버전 기준으로 하실거면 javax보다 jakarta 가 좋다고 하네요 이클립스에서 javax 이제 관리 안 한다구..
📝 JPQL 소개
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.getResultList();
// jpa는 코드를 짤 때 테이블 대상으로 짜지 않는다. 즉, Member객체를 대상으로 쿼리를 짠다
for(Member member : result){
System.out.println("member.name = " + member.getName());
}
// 결과물
select
m1_0.id,
m1_0.name
from Member m1_0 offset ? rows fetch first ? rows only
📝 JPA에서 가장 중요한 2가지
- 객체와 관계형 데이스 매팅파이(ORM)
- 영속성 컨텍스트
💡 영속성 컨텍스트란?
- "엔티티를 영구 저장하는 환경"이라는 뜻
- EntityManager.persist(entity);
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근
![]() | ![]() |
---|
📝 엔티티의 생명주기
//객체를 생성한 상태 (jpa와 전혀 관계 없는 상태)
Member member = new Member();
member.setId(1L);
member.setName("userA");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member)
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member)
em.remove(member);
📝 영속성 컨텍스트의 이점
// 비영속
Member member = new Member();
member.setId(101L);
member.setName("HelloJPA");
// 영속
em.persist(member); // 1차 캐시에 저장돼서 db에서 select 쿼리를 통해서 조회안함
Member findMember = em.find(Member.class, 101L);
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.name = " + findMember.getName());
Member findMember1 = em.find(Member.class, 101L); // 쿼리를 통해 조회
Member findMember2 = em.find(Member.class, 101L); // 1차 캐시를 통해 조회
결과물 : 쿼리 한 번만 조회됨
select
m1_0.id,
m1_0.name
from
Member m1_0
where
m1_0.id=?
System.out.println("result = " + (findMember2 == findMember1));
결과물
result = true
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
tx.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
tx.commit(); // [트랜잭션] 커밋
📝 플러시란?
📝 플러시 발생
📝 영속성 컨텍스트르 플러시하는 방법
💡 플러시를 하는 경우
: 지연SQL, 쓰기지연SQL에 저장된 쿼리를 DB로 전송하는 것이지 commit이 되는 것은 아니므로 commit까지 진행되어야 DB에 반영됨
📝 플러시 모드 옵션
em.setFlushMode(FlushModeType.COMMIT)
📝 준영속 상태란?
📝 준영속 상태롤 만드는 방법
📝 엔티티 매핑
1. 객체와 테이블 매핑 : @Entity, @Table
2. 필드와 칼럼 매핑 : @Column
3. 기본 키 매핑 : @Id
4. 연관관계 매핑 : @ManyToOne, @JoinColumn
📝 객체와 테이블 매핑
💡 주의
• 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)
• final 클래스, enum, interface, inner 클래스 사용X
• 저장할 필드에 final 사용 X
// persistence.xml 파일에 아래 문구 추가
<property name="hibernate.hbm2ddl.auto" value="create" />
// 파일 실행 시 기존 테이블 삭제 후 새로운 테이블 생성
drop table if exists Member cascade
create table Member (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
/* insert for
hellojpa.Member */insert
into
Member (name,id)
values
(?,?)
💡 주의
- 운영 장비에는 절대 create, create-drop, update 사용하면
안된다.- 개발 초기 단계는 create 또는 update
- 테스트 서버는 update 또는 validate
- 스테이징과 운영 서버는 validate 또는 none
💡 주의! ORDINAL 사용 X
→ 데이터 추가 시 위치에 따라 enum이 생성되기 때문에 운영시 문제생김
✅ EnumType.ORDINAL: enum 순서를 데이터베이스에 저장
✅ EnumType.STRING: enum 이름을 데이터베이스에 저장
💥 EnumType.STRING 개발하기