엔티티(Entity) 개념과 활용
1) 엔티티(Entity)란?
JPA에서 엔티티(Entity) 는 데이터베이스 테이블과 매핑되는 자바 클래스를 의미합니다. @Entity 어노테이션을 사용하여 선언하며, 반드시 @Id로 기본 키(Primary Key)를 지정해야 합니다.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name", nullable = false, length = 100)
private String name;
@Column(name = "email", unique = true)
private String email;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
}
3. 엔티티 연관 관계 매핑
1) 관계 매핑 종류 및 사용 예시
(1) 일대일 관계 (@OneToOne)
- 주로 상세 정보 테이블을 별도로 분리할 때 사용됩니다.
- 예: User - UserProfile (사용자와 프로필 정보)
@Entity
public class UserProfile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
(2) 일대다 / 다대일 관계 (@OneToMany, @ManyToOne)
- @ManyToOne은 일반적인 관계로, 대부분의 경우 외래 키(FK)가 존재하는 쪽에 설정합니다.
- @OneToMany는 주 테이블에서 여러 개의 하위 데이터를 관리할 때 사용됩니다.
- 예: User - Order (사용자와 주문 내역)
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
}
(3) 다대다 관계 (@ManyToMany)
- 중간 테이블을 만들어야 하며, 보통 @JoinTable을 활용합니다.
- 예: Student - Course (학생과 강의)
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses = new ArrayList<>();
}
4. Fetch 전략 (즉시 로딩과 지연 로딩)
1) FetchType.LAZY vs FetchType.EAGER
Fetch 유형설명
| FetchType.LAZY (지연 로딩) |
연관된 엔티티를 필요할 때만 조회 (기본값) |
| FetchType.EAGER (즉시 로딩) |
연관된 엔티티를 함께 조회 |
2) FetchType.LAZY (지연 로딩) 사용 예시
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
- user 엔티티는 실제 조회할 때까지 로드되지 않음 (프록시 객체로 유지)
- 성능 최적화에 유리하지만, LazyInitializationException 발생 가능성 있음
3) FetchType.EAGER (즉시 로딩) 사용 예시
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
private User user;
}
- user 엔티티를 Order 조회 시 즉시 불러옴 (JOIN 수행)
- 불필요한 데이터 조회로 성능 저하 가능성 있음
4) 어떤 경우에 LAZY 또는 EAGER를 사용할까?
| 시나리오 |
권장 Fetch 타입 |
| 성능 최적화가 중요한 경우 |
LAZY (기본값) |
| 반드시 연관된 엔티티가 필요할 경우 |
EAGER |
| 복잡한 객체 그래프 조회 시 |
JOIN FETCH 또는 EntityGraph 활용 |
5) JOIN FETCH를 활용한 최적화
@Query("SELECT o FROM Order o JOIN FETCH o.user WHERE o.id = :orderId")
Order findOrderWithUser(@Param("orderId") Long orderId);
- LAZY 설정이 되어 있어도 JOIN FETCH를 사용하여 즉시 조회 가능
6) EntityGraph를 활용한 최적화
@EntityGraph(attributePaths = {"user"})
@Query("SELECT o FROM Order o")
List<Order> findAllWithUserGraph();
- EntityGraph를 사용하면 특정 관계를 즉시 로딩할 수 있음