일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 1차원 DP
- 2차원 dp
- 99클럽
- @Builder
- @GeneratedValue
- @GenericGenerator
- @NoargsConstructor
- @Transactional
- Actions
- Amazon EFS
- amazon fsx
- Android Studio
- ANSI SQL
- ApplicationEvent
- assertThat
- async/await
- AVG
- AWS
- Azure
- bind
- builder
- button
- c++
- c++ builder
- c03
- Callback
- case when
- CCW
- chat GPT
- CICD
- Today
- Total
기록
[자바 ORM 표준 JPA 프로그래밍] JPA의 프록시와 연관관계 관리: 지연 로딩, 영속성 전이, 고아 객체 (8장) 본문
시작하면서
아래 내용은 "자바 ORM 표준 JPA 프로그래밍" 8장을 읽으면서 정리한 내용입니다. JPA(Java Persistence API)를 사용할 때 중요한 개념 중 하나는 '연관관계 관리'입니다. 연관된 엔티티를 효과적으로 로딩하고, 엔티티의 생명주기를 관리하는 것은 JPA를 활용하는 데 매우 중요한 요소입니다. 이번 포스팅에서는 프록시, 즉시 로딩과 지연 로딩, 영속성 전이, 고아 객체와 같은 주요 개념들을 다루어 보겠습니다.
1. 프록시와 지연 로딩
1.1 프록시란?
JPA에서는 데이터베이스에서 객체를 조회할 때, 실제 데이터를 필요한 순간까지 미루는 '지연 로딩(Lazy Loading)'이라는 방식을 사용할 수 있습니다. 이때 중요한 역할을 하는 것이 바로 프록시입니다.
프록시는 실제 엔티티 대신 사용되는 가짜 객체로, 데이터베이스에서 값을 가져오는 시점을 지연시키는 데 도움을 줍니다. 예를 들어, 회원(Member)과 팀(Team)의 연관관계가 있다고 가정해봅시다. 회원 정보를 가져왔는데 실제로 팀의 정보가 필요하지 않은 경우, 굳이 팀 엔티티까지 즉시 조회할 필요는 없습니다. 이럴 때 프록시 객체를 사용하여 팀 엔티티는 실제로 필요할 때만 조회하도록 할 수 있습니다.
Member member = em.getReference(Member.class, "member1");
위 코드에서 getReference
메서드를 사용하면 member1
에 대한 프록시 객체를 가져옵니다. 프록시 객체는 실제 데이터를 가지고 있지 않다가 member.getName()
과 같은 메서드를 호출할 때 데이터베이스에서 데이터를 조회하여 초기화합니다. 이를 프록시 초기화라고 합니다.
1.2 프록시와 식별자
프록시 객체는 엔티티의 식별자 값을 가지고 있습니다. 따라서 팀 객체의 식별자를 조회하는 team.getId()
와 같은 메서드를 호출하면 데이터베이스 조회 없이도 식별자 값을 반환할 수 있습니다. 프록시 객체는 생성 시점에 이미 식별자 값을 보관하고 있기 때문에, 이러한 경우에는 초기화가 필요하지 않습니다.
1.3 프록시 초기화 여부 확인
프록시 객체가 실제로 초기화되었는지 확인하려면 PersistenceUnitUtil.isLoaded(entity)
메서드를 사용할 수 있습니다. 이를 통해 특정 엔티티가 프록시 상태인지, 아니면 실제 데이터로 초기화되었는지 구분할 수 있습니다.
2. 즉시 로딩과 지연 로딩
2.1 즉시 로딩 (Eager Loading)
즉시 로딩은 엔티티를 조회할 때 연관된 엔티티도 함께 조회하는 방식입니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.EAGER) // 즉시 로딩 예시
@JoinColumn(name = "team_id")
private Team team;
// getter, setter
}
즉시 로딩을 설정하면 JPA는 연관된 엔티티를 조인 쿼리를 사용해 한 번에 가져오려고 합니다. 예를 들어, 회원과 팀을 조회할 때 회원과 팀을 조인하여 두 엔티티를 모두 조회하는 쿼리가 생성됩니다.
즉시 로딩은 사용하기 편리하지만, 필요 없는 데이터까지 한꺼번에 조회하게 되어 성능에 문제가 생길 수 있습니다. 특히 여러 컬렉션을 즉시 로딩할 경우 조인된 결과의 양이 많아져 성능 문제가 발생할 수 있으므로 주의가 필요합니다.
2.2 지연 로딩 (Lazy Loading)
지연 로딩은 실제로 엔티티가 필요할 때 데이터를 조회하는 방식입니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY) // 지연 로딩 예시
@JoinColumn(name = "team_id")
private Team team;
// getter, setter
}
위와 같이 설정하면 처음에는 프록시 객체를 반환하고, 엔티티의 데이터가 실제로 필요할 때 데이터베이스를 조회하여 초기화합니다. 이는 불필요한 쿼리 실행을 줄이고 성능을 최적화하는 데 유리합니다. 대부분의 경우 JPA에서는 지연 로딩을 기본으로 설정하고, 필요한 경우에만 즉시 로딩을 사용하는 것이 권장됩니다.
2.3 즉시 로딩과 지연 로딩 선택 기준
즉시 로딩을 선택하는 경우:
- 연관된 엔티티가 자주 함께 사용되며, 항상 필요한 경우 즉시 로딩을 사용하는 것이 좋습니다. 예를 들어, 회원을 조회할 때 항상 팀 정보도 함께 필요하다면 즉시 로딩을 통해 쿼리 횟수를 줄이고 한 번에 데이터를 가져올 수 있습니다.
- 애플리케이션의 초기 개발 단계에서 빠르게 작업하기 위해 성능을 고려하지 않고 모든 데이터를 한꺼번에 로딩하고자 할 때도 즉시 로딩이 편리할 수 있습니다.
지연 로딩을 선택하는 경우:
- 연관된 엔티티를 자주 사용하지 않거나, 특정 상황에서만 필요한 경우 지연 로딩이 성능에 더 유리합니다. 예를 들어, 회원의 팀 정보가 필요하지 않은 상황에서 불필요한 데이터베이스 조회를 피하기 위해 지연 로딩을 사용합니다.
- 규모가 큰 애플리케이션이나 복잡한 비즈니스 로직을 가진 시스템에서는 기본적으로 지연 로딩을 설정하여, 불필요한 쿼리 실행을 최소화하고 필요한 순간에만 데이터를 로딩하도록 최적화하는 것이 좋습니다.
일반적으로는 지연 로딩을 기본 설정으로 하고, 실제 사용하는 상황에 맞추어 꼭 필요한 경우에만 즉시 로딩을 사용하는 것이 가장 효율적입니다. 지연 로딩은 불필요한 쿼리 실행을 줄여 성능 최적화를 가능하게 하고, 사용 시점에 데이터를 로딩함으로써 효율적인 리소스 관리를 도와줍니다.
3. 영속성 전이와 고아 객체
3.1 영속성 전이 (Cascade)
영속성 전이는 부모 엔티티를 저장하거나 삭제할 때, 연관된 자식 엔티티도 함께 저장하거나 삭제하도록 설정하는 기능입니다. 예를 들어 부모 엔티티를 저장하면서 자식 엔티티도 함께 저장하려면 아래와 같이 설정할 수 있습니다.
@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
private List<Child> children = new ArrayList<>();
CascadeType.PERSIST
를 사용하면 부모 엔티티를 저장할 때 자식 엔티티도 자동으로 저장됩니다. 이를 통해 여러 엔티티를 개별적으로 저장해야 하는 번거로움을 줄일 수 있습니다.
3.2 고아 객체 (Orphan Removal)
고아 객체 제거 기능은 부모와의 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능입니다. 예를 들어 부모 엔티티의 컬렉션에서 자식 엔티티를 제거하면 해당 자식 엔티티는 고아가 되므로 자동으로 삭제됩니다.
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> children = new ArrayList<>();
이 설정을 사용하면 부모 엔티티와 자식 엔티티의 연관관계가 끊어졌을 때 자식 엔티티는 자동으로 삭제됩니다. 고아 객체 제거는 부모-자식의 생명주기가 명확하게 연결되어 있을 때 사용하면 유용합니다.
4. 영속성 전이 + 고아 객체: 생명주기 관리
만약 CascadeType.ALL
과 orphanRemoval = true
를 함께 사용하면 부모와 자식의 생명주기를 완전히 일치시킬 수 있습니다. 부모 엔티티가 저장될 때 자식도 저장되고, 부모가 삭제되면 자식도 삭제됩니다. 이러한 설정은 부모와 자식 간의 관계가 매우 강하게 연결되어 있을 때 적합합니다.
5. 정리
JPA에서 연관관계를 관리하는 방식은 애플리케이션의 성능과 유지보수성에 큰 영향을 미칩니다. 일반적으로는 지연 로딩을 사용하여 성능을 최적화하고, 필요한 경우에만 즉시 로딩을 사용해 개발의 편리함을 추가하는 것이 좋습니다. 또한 영속성 전이와 고아 객체 제거를 통해 부모-자식 간의 생명주기를 명확하게 관리하면 코드의 복잡도를 줄이고 일관성을 유지할 수 있습니다.
'교육 > 책' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 스프링 데이터 JPA 기능/쿼리 메서드, 페이징과 정렬, 벌크 연산 (12장-1) (0) | 2024.12.07 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] JPA 값 타입 - 기본 값 타입, 임베디드 타입, 값 타입 컬렉션 (9장) (0) | 2024.12.01 |
[자바 ORM 표준 JPA 프로그래밍] JPA 복합 키 매핑: @IdClass vs @EmbeddedId, 조인컬럼과 조인테이블 (7장-2) (0) | 2024.12.01 |
[자바 ORM 표준 JPA 프로그래밍] JPA 고급 매핑: 상속 관계 매핑(7장-1) (0) | 2024.12.01 |
[자바 ORM 표준 JPA 프로그래밍] 다양한 연관관계 매핑: 일대다, 다대일, 다대다 (6장) (0) | 2024.11.27 |