패러다임 불일치는 객체 지향 프로그래밍(OOP)과 관계형 데이터베이스(RDB) 간의 근본적인 차이점에서 발생하는 문제를 말한다.
객체 지향 프로그래밍은 데이터와 행동을 함께 갖는 객체를 중심으로 하는 반면,
관계형 데이터베이스는 데이터를 테이블 형태로 구조화한다.
이 두 시스템 사이의 차이로 인해 여러 문제가 발생할 수 있는데,
이 글에서는 JPA가 패러다임 불일치 문제를 해결하기 위해 어떠한 방법을 사용하는지 알아본다.
패러다임 불일치 문제와 JPA의 해결책
- 상속과 다형성
- (문제) 객체 지향 언어에서는 상속을 통해 재사용과 확장이 가능하지만, 관계형 데이터베이스는 이러한 계층적 구조를 직접적으로 지원하지 않는다.
- (해결) JPA는 클래스 계층을 DB 테이블과 매핑하는 여러 전략을 제공한다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String manufacturer;
// 생성자, getter, setter 생략
}
@Entity
public class Car extends Vehicle {
private int numberOfSeats;
// 생성자, getter, setter 생략
}
@Entity
public class Bike extends Vehicle {
private boolean hasBasket;
// 생성자, getter, setter 생략
}
@Inheritance 를 통해 매핑 전략을 지정할 수 있고, 이를 통해 상속 구조를 매핑할 수 있다.
이 예시는 단일 테이블(SINGLE_TABLE) 전략으로, 모든 클래스를 단일 테이블에 매핑하는 전략이다.
- 연관성 관리
- (문제) 객체는 참조를 통해 다른 객체와 연결되지만, 관계형 데이터베이스는 테이블 간의 관계를 외래 키를 통해 관리한다.
- (해결) JPA는 @OneToOne, @OneToMany, @ManyToOne, @ManyToMany 등의 어노테이션으로 객체 간의 관계를 정의하고, 이를 데이터베이스 테이블 관계로 변환한다.
@Entity
public class User {
@OneToMany(mappedBy = "user") // 1:N 관계
private List<Order> orders;
}
@Entity
public class Order {
@ManyToOne // N:1 관계
@JoinColumn(name = "user_id")
private User user;
}
@ManyToOne, @OneToMany 어노테이션으로 User 엔티티와 Order 엔티티 사이의 관계를 설정했다.
이러한 매핑을 통해 객체 지향적인 방식으로 데이터베이스의 관계를 표현할 수 있다.
- 식별성 문제
- (문제) 객체의 동일성은 인스턴스에 의해 결정되지만, 관계형 데이터베이스에서는 행의 동일성이 주로 기본 키에 의해 결정된다.
- (해결) JPA는 @Id, @EmbeddedId, @IdClass 등의 어노테이션으로 객체의 식별자(ID)를 지정한다.
@Entity
public class Product {
@Id
private Long id; // 기본 키 필드
// 나머지 필드 및 메소드
}
위 예시에서 @Id 어노테이션은 id 필드가 해당 엔티티의 기본 키(Primary Key) 임을 나타낸다.
@Embeddable
public class OrderId implements Serializable {
private Long userId;
private Long orderId;
// 생성자, getter, setter, equals, hashCode 생략
}
@Entity
public class Order {
@EmbeddedId
private OrderId id; // 복합 키
// 나머지 필드 및 메소드
}
@EmbeddedId 어노테이션을 사용하면 복합 키를 가지는 엔티티의 식별자를 정의할 수 있다.
복합키를 구성하는 필드를 가지는 별도 클래스를 정의하고, 엔티티는 이 클래스를 복합 키로 사용한다.
- 데이터 내비게이션
- (문제) 객체 지향 언어에서는 객체 참조를 통해 쉽게 다른 객체로 이동할 수 있지만, 데이터베이스에서는 조인 연산을 통해 관련 데이터에 접근해야 한다.
- (해결) JPA는 지연 로딩과 즉시 로딩 전략을 제공하고, 이를 통해 필요에 따라 관련 객체를 자동으로 로드한다.
@Entity
public class User {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders; // 사용자가 가진 주문들
}
@Entity
public class Order {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user; // 이 주문을 소유한 사용자
}
위 코드에서 User 엔티티는 여러 Order 1:N의 관계를 가지고 있는데,
fetch 속성을 통해 해당 관계의 엔티티를 언제 로딩할 것인지를 결정할 수 있다.
FetchType.LAZY는 지연 로딩을 의미하며, 이는 관련 데이터가 실제로 필요할 때까지 로딩을 지연시킨다.
반대로 FetchType.EAGER는 즉시 로딩을 의미하며, 엔티티가 로드될 때 관련 데이터도 함께 로드된다.
이 로딩 전략을 적절히 활용해 애플리케이션의 성능을 최적화하는 것이 바람직하다.
(기본적으로 지연 로딩 전략을 채택하고, 필요한 경우에만 즉시 로딩을 사용한다던지)
'Framework > JPA' 카테고리의 다른 글
[JPA] @Converter 암호화를 사용해 데이터베이스 보안 강화하기 (0) | 2024.08.24 |
---|---|
[JPA] JPA(Java Persistence API)란? (0) | 2024.01.02 |