EntityManagerFactory
에서 이뤄지며, Spring에서는 트랜잭션 단위로 생성 소멸된다.EntityManager
를 직접 쓰지 않고, Repository를 통해 간접적으로 사용JPA의 핵심적인 기능으로, 엔티티를 효율적으로 관리하고 객체와 데이터베이스 간의 패러다임 불일치를 해결하는 데 중요한 역할
UPDATE
SQL 자동 수행OSIV(Open Session In View)란?
OSIV는 HTTP 요청이 시작될 때부터 응답이 완료될 때까지 영속성 컨텍스트(EntityManager, 즉 Hibernate의 Session)를 열어 두는 전략
OSIV가 없으면? -> HTTP 요청 초반부인 Service Layer에서만 트랜잭션과 영속성 컨텍스트가 살아 있다. 따라서 Controller나 View에서 Lazy Loading을 시도하면 LazyInitializationException 발생 ⚠️
👉 실무에서는 어떻게 할까?
spring.jpa.open-in-view=false
)Propagation(전파 레벨)
트랜잭션 전파(Propagation)은 스프링에서 하나의 트랜잭션이 진행 중일 때, 다른 트랜잭션을 호출하면 어떻게 동작할지를 정의하는 것.
→ 즉, A 서비스 메서드가 트랜잭션을 갖고 있을 때, 내부에서 B 메서드(또는 다른 서비스)를 호출하면,
B는 A의 트랜잭션을 공유할지, 새로 만들지, 아니면 예외를 던질지를 설정하는 게 전파 속성(Propagation)이다.
@Transactional
public void outerMethod() {
try {
innerService.innerMethod(); // 별도 트랜잭션
} catch (Exception e) {
// outer는 rollback 안 함
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {
// outer 트랜잭션과는 독립적
}
스프링에서 @Transactional
로 선언적 트랜잭션을 쓸 때, propagation
옵션으로 전파 방식을 제어
IllegalTransactionStateException
)한 번의 조회로 주 엔티티 “N개”를 가져왔을 때, 각각의 연관 엔티티를 다시 조회하는 쿼리가 “N번” 추가 실행되어 총 (1 + N)번의 쿼리가 발생하는 현상
public void printTeamMembers(EntityManager em) {
List<Team> teams = em.createQuery("SELECT t FROM Team t", Team.class)
.getResultList(); // 1️⃣ 1번 쿼리 (Team 전체 조회)
for (Team team : teams) {
System.out.println("Team: " + team.getName()); // 2️⃣ N(팀수)번 쿼리 발생
for (Member member : team.getMembers()) {
System.out.println(" Member: " + member.getUsername());
}
}
}
⬇️
for (Member member : team.getMembers()) {
System.out.println(" Member: " + member.getUsername());
}
// 여기서 다음과 같은 쿼리 발생 SELECT * FROM member WHERE team_id = ?
👉 여기서 무슨 일이 벌어지냐면?
SELECT * FROM team
→ 팀 전체 조회 (1번)SELECT * FROM member WHERE team_id = ?
쿼리 발생 (팀 수 만큼 N번)