프로젝트 생성
H2 데이터베이스는 기존에 사용하고 있었기에 신규 DB 파일만 생성하고 Java 프로젝트를 생성한다. 강의에서는 Java 8에 빌드도 Maven으로 설정하고 있으나, 깔린대로 해도 문제가 없을거 같아서 Java 21에 빌드는 Gradle로 설정했다.
plugins {
id("java")
}
group = "jpa-basic"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
//spring-boot-starter-jpa 를 보통 많이 쓴다고 하지만,
//강의에서는 maven 에 hibernate 와 h2 database 를 따로 추가했다.
implementation("org.hibernate.orm:hibernate-core:7.0.6.Final") //hibernate
implementation("com.h2database:h2:2.3.232") //h2 database
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
}
tasks.test {
useJUnitPlatform()
}
JPA 구현체인 Hibernate와 사용 할 데이터베이스인 H2 데이터베이스의 의존성을 추가해준다. 스프링 부트였다면 spring-boot-starter-jpa
만 추가해도 두 개 다 포함하고 있으나, 이번 강의는 스프링을 사용하지 않기 때문에 제외한다.
JPA 설정 파일
JPA를 사용하기 위해서는 persistence.xml
이라는 설정 파일이 필요하다. 기본적으로 META-INF
디렉토리 하위에 위치하고, persistence-unit
의 name
으로 이름을 지정하여 사용한다. 속성 중에 javax.persistence
로 시작하는 속성은 JPA 표준 속성으로 구현체의 영향을 받지 않고, hibernate
로 시작하는 속성은 Hibernate에서만 사용할 수 있는 속성이다.
<?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">
<!-- 순수한 JPA 프로젝트에서는 Entity 클래스를 명시적으로 등록 -->
<class>hellojpa.Member</class>
<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/~/Downloads/spring/jpa-basic/jpa"/>
<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 Dialect(방언)
JPA의 Dialect(방언)은 JPA 구현체가 어떤 데이터베이스에 맞는 SQL을 생성해야 하는지 알려주는 설정이다. JPA는 특정 데이터베이스에 종속되어있지 않고, SQL은 표준이 있지만 DB마다 문법·함수·데이터타입 등이 다르기 때문에 필요하다. 위의 JPA 설정 파일에서도 hibernate.dialect
속성을 H2 데이터베이스에 맞추었다. 만약 MySQL이나 Oracle을 사용한다면 사용하는 데이터베이스에 맞추면 된다.
기본 코드 생성
package hellojpa;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
//애플리케이션에 하나만 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
//트랜잭션 필요 시 생성 및 close, 쓰레드 간 공유 X
EntityManager em = emf.createEntityManager();
//JPA 의 모든 데이터 변경은 트랜잭션 안에서 실행
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
/* Create
Member member = new Member();
member.setId(2L);
member.setName("2Name");
em.persist(member);
*/
/* Update
//JPA 를 통해서 객체를 가져오면 persist 할 필요가 없다.
Member findMember = em.find(Member.class, 1L);
findMember.setName("Name1");
*/
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.getResultList();
for(Member member : result) {
System.out.println("member.name = " + member.getName());
}
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
JpaMain.java
를 생성하고 EntityManagerFactory
와 EntityManager(em)
를 생성하고, 생성한 em
에서 트랜잭션을 얻어와 그 안에서 데이터 변경을 실행한다. 트랜잭션이 종료되면 결과에 따라 commit 또는 rollback하게 되고, em
을 반환하게 된다. 주석으로 작성하였 듯이 EntityManagerFactory
는 애플리케이션에 하나만 생성하고, em
은 트랜잭션 필요 시 생성하고 반환하면 된다.
=> EntityManagerFactory
는 데이터베이스 연결 설정, JPA 메타데이터 파싱, EntityManager 생성 등 무거운 리소스를 관리하는 역할이다. 생성 비용이 매우 크고, 내부적으로 커넥션 풀, 캐시, 설정 정보 등을 보유한다. 또한 여러 스레드에서 동시에 접근해도 안전하게 설계(Thread-safe)되어 있지만, 여러 개를 만들면 시스템 자원을 낭비하고, 성능 저하 및 예기치 않은 동시성 문제가 발생할 수 있어 애플리케이션에 하나만 생성하고 공유하는 것이 가장 효율적이다.
=> EntityManager
는 실제로 Entity를 관리(CRUD)하며, 영속성 컨텍스트※를 관리한다. 생성 비용이 크지 않고, 가볍다. 하지만 여러 스레드에서 동시에 접근하면 데이터 불일치, 예기치 않은 버그가 발생할 수 있다(Non-thread-safe). EntityManager
는 트랜잭션 범위 내에서 Entity의 상태를 관리하는데, 트랜잭션이 끝나면 EntityManager
와 함께 영속성 컨텍스트도 종료되어야 일관성이 보장된다. 다음과 같은 이유로 필요 시 생성하고, 공유하지 않는다.
- 동시성 문제: 여러 트랜잭션이 하나의 `EntityManager`를 공유하면, 서로 다른 작업이 섞여 데이터 정합성이 깨진다.
- 영속성 컨텍스트 오염: 이전 트랜잭션의 데이터가 남아있어 잘못된 결과를 반환할 수 있다.
- JPA 명세: `EntityManager`는 트랜잭션 단위로 생성/사용/종료하는 것이 명확하게 권장된다.
※ 영속성 컨텍스트(Persistence Context)는 JPA에서 Entity 객체를 영구적으로 저장하고 관리하는 메모리 공간을 의미하는 핵심 개념 중 하나이다. Entity 생명주기 관리, 1차 캐시, 동일성 보장, 변경 감지(Dirty Checking), 쓰기 지연(Write-behind), 지연 로딩(Lazy Loading) 등의 주요 특징이 있다.
package hellojpa;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Member {
@Id
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity
어노테이션이 있어야 JPA가 관리하는 객체가 된다. 순수한 Java 프로젝트를 생성했기 때문에 persistence.xml
에 <class>hellojpa.Member</class>
와 같이 Entity 클래스를 등록해줘야 인식한다.
JPQL
JPQL(Java Persistence Query Language)는 JPA에서 사용하는 객체지향 쿼리 언어다. 가장 큰 특징은 데이터베이스의 테이블이 아니라 Entity 객체를 대상으로 쿼리를 작성한다는 점인데, 검색도 테이블이 아니라 Entity 객체를 대상으로 검색한다. SQL을 추상화하기 때문에 특정 데이터베이스에 종속되지 않으며, JPA가 JPQL을 실제 DB에 맞는 SQL로 변환해서 실행한다.
...
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.getResultList();
...
위에서 작성된 모든 Member Entity를 검색하는 JPQL이다.
https://github.com/hongbre/jpa-basic
GitHub - hongbre/jpa-basic
Contribute to hongbre/jpa-basic development by creating an account on GitHub.
github.com
'스터디 > JPA' 카테고리의 다른 글
JPA 스터디2 - 1 (1) | 2025.07.13 |
---|---|
JPA 스터디1 - 3 (0) | 2025.07.01 |
JPA 스터디1 - 2 (3) | 2025.06.16 |
JPA 스터디1 - 1 (1) | 2025.06.10 |