JPA 설정 및 동작 방식(예시 포함)

D A S H B O A R D
D E V E L O P
S E C U R I T Y
 JPA란?
 JPA 동작 방식
 JPA 표준 명세
 Spring 없이 순수한JPA 설정
 Spring 없이 순수한 JPA 사용
주요내용
Reference

 JPA란?

JPA는 JAVA Persistence API의 약자로 자바 진영에서 ORM(Object-Relational Mapping) 기술의 표준 인터페이스를 제공하는 API이다.
ORM은 객체 지향 프로그래밍에서 사용하는 객체와 관계형 데이터베이스의 데이터를 매핑하여 개발자가 객체 지향적으로 데이터를 다룰 수 있도록 도와주는 기술이다.
JPA는 이러한 ORM 기술을 자바 언어에서 구현할 수 있도록 표준 인터페이스를 제공하며, 이를 구현하는 다양한 ORM 프레임워크가 있다.
ORM - Object-relational mapping(객체 관계 매핑)
객체는 객체대로 설계
관계형 데이터베이스는 관계형 데이터베이스대로 설계
ORM 프레임워크가 중간에서 매핑
대중적인 언어에는 대부분 ORM 기술이 존재

 JPA 동작 방식

JPA는 JAVA 어플리케이션과 JDBC API 사이에서 동작한다.
위에서 말한 JAVA 진영의 ORM 표준의 의미로 JPA가 JAVA 어플리케이션의 Entity를 받아 분석하고 SQL을 생성, 그리고 JDBC의 API를 사용해 통신한다. ⇒ 즉, JPA가 Entity를 받아 분석하고 SQL을 생성하기 때문에, ORM이라고 하는 것이다.

예 - JPA를 이용한 데이터 저장 방식

1.
JAVA 어플리케이션은 Entity Object라는 객체를 등록하고자 JPA에 던져준다.
2.
JPA는 해당 Entity(객체)를 분석하고 INSERT 구문을 생성한다.
3.
생성된 SQL을 JDBC API를 이용해 데이터를 저장한다.
패러다임 불일치를 해결한다?
DB마다 사용하는 문법들이 다르다. 예를 들면 MySQL에서 Limit이 Oracle에서는 RowNum으로 사용되는 것과 같이 다르기 때문에, 해당 구문들을 JPA가 알아서 해석해 SQL 구문을 생성해내기 때문에 패러다임 불일치를 해결해준다고 표현한다.

 JPA 표준 명세

JPA는 인터페이스의 모음이다.
JPA 2.1 표준 명세를 구현한 3가지 구현체
Hibernate, EclipseLink, DataNucleus
보통 Hibernate를 사용!!
2023 현재 JPA 최신버전은 2022년 봄에 나온 3.1버전으로 Jakarta EE 10의 일부로 출시되었다.

 Spring 없이 순수한 JPA 설정

Database는 h2 database를 이용

maven 프로젝트 생성(Gradle은 spring boot에 의존하기 때문에 연습용!)

<dependencies> <!-- JPA 하이버네이트 --> <dependency> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-core</artifactId> <version>6.1.7.Final</version> </dependency> <!-- H2 데이터베이스 --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>2.1.214</version> </dependency> </dependencies>
XML
복사
우린 JPA 표준인터페이스에 Hibernate 구현체를 사용 ⇒ 사용 버전은 현재 spring boot에서 지원하고 있는 버전과 맞추기! ⇒ 아래 페이지에서 org.hibernate를 검색해서 찾아보면 버전을 찾을 수 있다.
DB는 h2 database를 사용하기 때문에 해당 dependency 추가 ⇒ 버전은 다운받은 h2 db의 버전을 꼭 잘 맞춰주자!!

JPA 설정하기 - persistence.xml

<?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>
XML
복사
JPA 설정 파일• /META-INF/persistence.xml 위치
persistence-unit name으로 이름 지정
javax.persistence로 시작: JPA 표준 속성 ⇒ JAVA 3.x 부터는 jakarta.persistence로 시작
hibernate로 시작: 하이버네이트 전용 속성
<persistence-unit> : name에 DB이름 지정
javax.persistence.jdbc.driver : DB driver 지정(h2면 h2, MySQL이면 MySQL과 같이)
hibernate.dialect : 위에서 JPA가 패러다임의 불일치를 해결해준다고 했는데 이 dialect를 이용해 각 DB별로 구문을 구분해 생성해준다.
dialect 도 DB 별로 지정(h2면 h2, MySQL이면 MySQL과 같이)

 Spring 없이 순수한 JPA 사용

DB 생성

create table Member ( id BIGINT NOT NULL, name VARCHAR(255), PRIMARY KEY (id) );
SQL
복사

Member 객체 생성

@Entity 어노테이션을 사용해 JPA가 관리할 객체임을 명시
@Id 어노테이션을 사용해 데이터베이스 PK와 매핑
테이블 및 컬럼 지정
@Table(name = “테이블 명”)
@Column(name = “컬럼 명”) ⇒ 지정하지 않을 경우 변수명과 같은 컬럼에 저장
package hellojpa; import jakarta.persistence.Entity; import jakarta.persistence.Id; @Entity // JPA가 관리할 객체를 의미하는 것으로 JPA 사용할 경우 꼭 넣어줘야함 // @Table(name = "Member") public class Member { @Id private Long id; // @Column(name = "nameId") private String name; public void setId(Long id) { this.id = id; } public void setName(String name) { this.name = name; } public Long getId() { return id; } public String getName() { return name; } }
Java
복사

JpaMain 생성

1.
Persistence.createEntityManagerFactory("hello"); ⇒ Persistence.createEntityManagerFactory를 이용해 hello DB의 Entity MangerFactory를 생성해야 한다.
2.
emf.createEntityManager(); ⇒ EntityManagerFactory를 통해 EntityManager를 호출한다. ⇒ EntityManager를 호출하는 것은 DB와의 커넥션을 하나 생성하는 것을 의미
3.
EntityTransaction tx = em.getTransaction(); ⇒ 모든 JPA 통신은 Transaction 안에서 이루어져야 한다. ⇒ 그렇기 때문에 Transaction 생성 후 begin()을 통해 Transaction 시작
4.
em.persist(member);EntityManager 를 통해 객체(Entity)를 영속성 컨텍스트에 저장(persist)한다.
5.
tx.commit() ⇒ Transcation을 끝내면서 SQL을 전송 ⇒ persist로 등록이 된 상태라도 commit을 하지 않으면 SQL이 적용되지 않는다.
6.
em.close(); ⇒ Transaction이 끝나면 항상 닫아줘야 한다. ⇒ 그래야 DB와의 통신이 종료됨을 의미
7.
emf.close(); ⇒ 모든 Data관련 작업이 끝나면 EntityMangerFactory 도 닫아준다.
tx.rollback();
위 try구문에서 에러가 날 경우 transaction을 롤백해 SQL 구문이 적용되지 않도록 한다.
영속성 컨텍스트란? ⇒ 화면 아래 버튼 클릭!
package hellojpa; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.EntityTransaction; import jakarta.persistence.Persistence; public class JpaMain { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); try { Member member = new Member(); member.setId(1L); member.setName("HelloA"); em.persist(member); tx.commit(); } catch (Exception e) { tx.rollback(); } finally { // EntityManager는 Transaction이 끝나면 항상 닫아줘야 한다. em.close(); } emf.close(); } }
Java
복사

실제 JPA 사용에서는 Spring이 많은 것을 도와주기 때문에 코드가 많이 줄어든다.

조회 및 수정

public class JpaMain { public static void main(String[] args) { 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(); } }
Java
복사
persist를 굳이 호출하지 않아도 됨 ⇒ Transaction Commit때 변경사항이 있다면 알아서 persist를 진행해줌

JPQL을 사용한 실습

객체 단위로 DB를 조회할 수 있다.
전체 회원 조회
List<Member> result = em.createQuery("select m from Member as m", Member.class) .getResultList(); tx.commit();
Java
복사
페이징 → 1번부터 10개 가져오는 코드
List<Member> result = em.createQuery("select m from Member as m", Member.class) .setFirstResult(1) .setMaxResults(10) .getResultList(); tx.commit();
Java
복사