[JPA] PersistenceUnit: EntityManagerFactory의 정의 단위
정의
PersistenceUnit 은 하나의 논리적 DB 연결 + entity 매핑 집합 을 정의한 단위. 결과물은 EntityManagerFactory 1개.
JPA 아키텍처의 계층:
PersistenceUnit ← 설정 단위 (persistence.xml 또는 Spring config)
↓ 생성
EntityManagerFactory ← 무거운 thread-safe 객체, 앱 1개
↓ createEntityManager()
EntityManager ← 가벼운 단일 thread 객체, 트랜잭션마다 새로
↓ 보유
PersistenceContext ← 1차 캐시 + dirty checking
spring-jpa-persistence-context 가 트랜잭션 범위의 메모리 저장소를 다룬다면, 이 페이지는 앱 시작 시점의 설정 / factory 단위 를 다룬다.
비교 페이지: spring-jpa-context-vs-unit.
persistence.xml (표준 Jakarta Persistence)
순수 JPA 환경의 정석:
<!-- src/main/resources/META-INF/persistence.xml -->
<persistence xmlns="https://jakarta.ee/xml/ns/persistence" version="3.1">
<persistence-unit name="primaryPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.example.User</class>
<class>com.example.Order</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost/db"/>
<property name="jakarta.persistence.jdbc.user" value="app"/>
<property name="jakarta.persistence.jdbc.password" value="secret"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
</persistence>
EntityManagerFactory emf = Persistence.createEntityManagerFactory("primaryPU");
EntityManager em = emf.createEntityManager();
Spring Boot 환경에선 직접 작성 거의 안 함. Spring 이 LocalContainerEntityManagerFactoryBean 으로 동등한 기능 제공.
Spring Boot 의 PersistenceUnit (자동)
Spring Boot 의 JpaAutoConfiguration 이 자동 생성:
spring:
datasource:
url: jdbc:postgresql://localhost/db
username: app
password: ${DB_PASSWORD}
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
hibernate:
ddl-auto: validate
show-sql: false
내부적으로 LocalContainerEntityManagerFactoryBean Bean 자동 등록. persistence.xml 없이도 entity 자동 스캔 (@SpringBootApplication 패키지 기준).
명시적 등록:
@Configuration
public class JpaConfig {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource,
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(dataSource)
.packages("com.example.domain")
.persistenceUnit("primary")
.properties(Map.of(
"hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect",
"hibernate.hbm2ddl.auto", "validate"
))
.build();
}
@Bean
PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
transaction-type: RESOURCE_LOCAL vs JTA
<persistence-unit name="..." transaction-type="RESOURCE_LOCAL">
<persistence-unit name="..." transaction-type="JTA">
| 타입 | 트랜잭션 관리 | 사용처 |
|---|---|---|
RESOURCE_LOCAL | JPA 가 직접 (em.getTransaction().begin/commit) | 일반 Spring Boot, 단일 DB |
JTA | 외부 Transaction Manager (Java EE container, Atomikos, Narayana) | XA 분산 트랜잭션, EJB |
Spring Boot 의 기본은 RESOURCE_LOCAL + JpaTransactionManager (Spring 의 transaction abstraction). JTA 는 별도 설정 필요 + 거의 안 씀.
@PersistenceUnit 어노테이션
EntityManagerFactory 자체를 주입받을 때:
@Service
public class MyService {
@PersistenceUnit(unitName = "primary")
private EntityManagerFactory emf;
public void doWork() {
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
// ...
em.getTransaction().commit();
} finally {
em.close();
}
}
}
거의 안 씀. Spring Boot 에선 @PersistenceContext 또는 Repository 패턴 권장.
@PersistenceUnit vs @PersistenceContext:
@PersistenceUnit→EntityManagerFactory주입 (factory)@PersistenceContext→EntityManager주입 (PC handle, 트랜잭션 scope)
복수 PersistenceUnit
서로 다른 DB / entity set 사용 시:
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.orders",
entityManagerFactoryRef = "ordersEmf",
transactionManagerRef = "ordersTm"
)
public class OrdersJpaConfig {
@Bean
LocalContainerEntityManagerFactoryBean ordersEmf(
EntityManagerFactoryBuilder builder,
@Qualifier("ordersDataSource") DataSource ds) {
return builder.dataSource(ds)
.packages("com.example.orders")
.persistenceUnit("orders")
.build();
}
@Bean
PlatformTransactionManager ordersTm(@Qualifier("ordersEmf") EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.analytics",
entityManagerFactoryRef = "analyticsEmf",
transactionManagerRef = "analyticsTm"
)
public class AnalyticsJpaConfig {
@Bean
LocalContainerEntityManagerFactoryBean analyticsEmf(
EntityManagerFactoryBuilder builder,
@Qualifier("analyticsDataSource") DataSource ds) {
return builder.dataSource(ds)
.packages("com.example.analytics")
.persistenceUnit("analytics")
.build();
}
@Bean
PlatformTransactionManager analyticsTm(@Qualifier("analyticsEmf") EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
각 entity 패키지는 분리. @Repository 도 각 패키지 안. spring-boot-multi-datasource 참고.
EntityManagerFactory 의 무게
EntityManagerFactory emf = Persistence.createEntityManagerFactory("primaryPU");
내부에서 일어나는 일:
- 모든
@Entity클래스 스캔 + 메타데이터 분석 - mapping XML 파싱 (있다면)
- second-level cache 초기화
- query 사전 컴파일 (named queries)
- connection pool 초기화
비싸다. 앱 시작 시 1번만 생성. close 안 하면 JVM 종료까지 살아 있음. 절대 createEntityManagerFactory 를 반복 호출하지 말 것.
EntityManager 는 가벼움 (PC 1개 + Connection 1개 reference). 트랜잭션마다 새로 생성하고 즉시 close.
Bootstrap Mode
spring:
jpa:
bootstrap-mode: deferred # default / lazy / deferred
| Mode | 동작 | 사용처 |
|---|---|---|
default | 시작 시 동기적으로 EMF 생성 | 보장된 startup, 작은 앱 |
lazy | 첫 사용 시점에 생성 | 시작 시간 단축, 첫 요청 느림 |
deferred | 백그라운드 thread 로 비동기 생성, 사용 시점에 대기 | Spring Boot 권장 default |
deferred 가 보통 최선. 대형 앱 (entity 수백개) 의 시작 시간 단축 + 첫 요청 안 막힘.
Properties (Hibernate 튜닝)
spring:
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
use_sql_comments: true
jdbc:
batch_size: 50 # batch INSERT/UPDATE
batch_versioned_data: true
time_zone: UTC
fetch_size: 100
order_inserts: true
order_updates: true
connection:
provider_disables_autocommit: true
cache:
use_second_level_cache: true
region.factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
query:
plan_cache_max_size: 2048
plan_parameter_metadata_max_size: 128
statistics: false # 운영 환경에선 보통 false
자주 쓰는 옵션:
batch_size: 50: INSERT/UPDATE batch 처리 (성능 큰 영향)order_inserts: true: batch hit 률 ↑provider_disables_autocommit: true: HikariCP 와 조합 시 connection 획득 최적화format_sql,use_sql_comments: dev 만, 운영은 비활성
Entity 스캔 범위
@Bean
LocalContainerEntityManagerFactoryBean emf(EntityManagerFactoryBuilder builder, DataSource ds) {
return builder.dataSource(ds)
.packages("com.example.domain", "com.example.shared") // 명시
.persistenceUnit("primary")
.build();
}
@EntityScan 어노테이션도 가능:
@SpringBootApplication
@EntityScan(basePackages = "com.example.domain")
class App {}
명시 안 하면 @SpringBootApplication 패키지 + 하위만.
Persistence Provider
JPA 는 spec, 실제 구현은 provider:
spring:
jpa:
properties:
hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
| Provider | 특징 |
|---|---|
| Hibernate (기본) | 가장 일반, Spring Boot 의 기본 |
| EclipseLink | Jakarta EE reference impl |
| OpenJPA | Apache, deprecated 흐름 |
대부분 Hibernate. 다른 provider 교체는 거의 안 일어남.
함정과 베스트 프랙티스
EntityManagerFactory는 앱 1개: 반복 생성 = 메모리 누수- Spring Boot 에선 persistence.xml 거의 불필요:
LocalContainerEntityManagerFactoryBean이 동등 - bootstrap-mode: deferred 권장: 시작 시간 단축
hibernate.jdbc.batch_size설정: 대량 INSERT 시 큰 효과- 여러 PersistenceUnit 사용 시 패키지 분리 필수: entity 가 어느 PU 에 속하는지 명확히
@PersistenceUnitvs@PersistenceContext혼동 주의: 후자가 보통 정답- transaction-type 은 RESOURCE_LOCAL: JTA 는 특수 상황만
- 2차 캐시 신중: 멀티 인스턴스에서 stale 위험, 정말 read-heavy 한 항목만
관련 위키
이 글의 용어 (7개)
- [JPA] PersistenceContext vs PersistenceUnit 비교spring
- 정의 JPA 의 두 개념은 레벨이 다르다. 이름이 비슷해서 자주 혼동하지만: - PersistenceUnit = 설정 단위 (정적, 앱 1번) - PersistenceContex…
- [JPA] PersistenceContext: 1차 캐시, dirty checking, flushspring
- 정의 PersistenceContext 는 가 관리하는 entity 의 메모리 저장소 + 변경 추적기. 트랜잭션 안에서만 살아 있다 ( 진입 시 생성, commit/rollbac…
- [Spring Boot] Database Initialization: schema.sql, Flyway, Liquibasespring
- 정의 DB 스키마 / 데이터를 application 시작 시점에 자동 설정하는 메커니즘. 세 가지 접근: 1. / (Spring Boot 내장, 단순) 2. Hibernate (…
- [Spring Boot] Hibernate Naming Strategyspring
- 정의 Hibernate 가 entity 클래스 / 필드명 → DB table / column 명으로 변환하는 규칙. 두 단계로 분리: | 단계 | 역할 | 인터페이스 | |---…
- [Spring Boot] Multi-DataSource: 복수 DB 연결과 트랜잭션spring
- 정의 한 Spring Boot 앱이 여러 DB / 여러 스키마 에 접근. 패턴 세 가지: 1. 물리적으로 분리된 DB (orders DB + analytics DB) 2. Rea…
- [Spring] Spring Data JPA: Repository, Query Methodsspring
- 정의 Spring Data JPA는 JPA(Hibernate) 위에 Repository 추상화를 얹어 boilerplate를 제거. 인터페이스만 정의하면 Spring이 구현체 자…
- [Spring] Transaction: @Transactional, propagation, isolationspring
- 정의 Spring 트랜잭션은 AOP 기반 선언형 트랜잭션 관리. 한 줄로 메서드 전체를 트랜잭션 범위로. JDBC, JPA, MongoDB 등 backend에 동일 API. 기본…
💬 댓글