[프로그래밍] JPA

[JPA] JPA 와 연관관계

JHVan 2024. 5. 5. 18:49

연관관계

JPA에서 영속 객체(Entity) 간의 연관관계를 통해 객체 지향 프로그래밍에서의 객체들 간 관계와 데이터베이스의 테이블 간 관계를 서로 매핑하여 연결함.

영속객체가 테이블과 매핑되는 것 처럼 영속객체간의 관계는 테이블간의 관계와 매핑됨.

영속객체간 관계 그리고 테이블간 관계에는 차이가 있어 이해하고 매핑을 구성해야함.

객체와 테이블 연관관계

 

객체 참조 vs. 외래 키 참조

  • 객체 지향 프로그래밍에서는 객체 간의 관계가 객체 참조를 사용하여 표현. User 객체가 여러 개의 Order 객체를 참조할 수도 있음.
  • 관계형 데이터베이스에서는 테이블 간의 관계가 외래 키를 사용하여 표현됨.  'Orders' 테이블에는 'User' 테이블의 기본 키를 참조하는 외래 키가 있을 수 있음.

방향성(Direction)

  • 단방향(One-way): 한 영속 객체에서 다른 영속 객체로의 관계만 정의된 경우.  반대 방향으로의 접근이 필요할 경우에는 적합하지 않음.
  • 양방향(Bi-directional): 두 영속 객체가 서로를 참조하는 관계.  한쪽을 주인(Owner)으로 지정하여 연관관계의 관리를 단순화.

다중성(Multiplicity)

  • 일대일(1:1): 두 영속 객체가 1:1 관계를 가지는 경우. 
  • 일대다(1:N): 한 영속 객체가 다른 여러 영속 객체와 관계를 가지는 경우
  • 1:N 관계에서 외래키는 데이터베이스의 두 테이블 간 관계를 표현하는 중요한 메커니즘
    이 관계에서 '1' 쪽은 부모 테이블, 'N' 쪽은 자식 테이블.
    자식 테이블에는 부모 테이블의 기본키를 참조하는 외래키가 포함되어 있음.
    두 테이블 간의 관계를 관리하고, 데이터의 무결성을 유지하기 위함.
    
    외래키의 역할
    데이터 연결: 외래키는 자식 테이블의 레코드를 부모 테이블의 관련 레코드에 연결.
    관계형 데이터베이스에서 관계를 표현하고, JOIN 연산을 사용하여 관련 데이터를 함께 쿼리함.
    데이터 무결성 유지: 외래키 제약조건을 사용하여 데이터 무결성을 유지.
    (ex:부모 테이블에 없는 기본키 값을 자식 테이블의 외래키로 사용할 수 없도록 제한하여,
    데이터의 정확성을 보장.)
    
    연쇄 작업: 외래키 제약조건을 통해 연쇄 삭제(CASCADE DELETE)나 연쇄 업데이트(CASCADE UPDATE) 같은
    연쇄 작업을 정의할 수 있음. 
    부모 테이블의 특정 레코드가 삭제되거나 업데이트될 때 
    자동으로 자식 테이블의 관련 레코드도 삭제되거나 업데이트 됨.
    
    외래키의 구현
    SQL에서 1:N 관계를 구현할 때는 다음과 같은 방식으로 외래키를 설정할 수 있음.
    CREATE TABLE User (
        UserID INT PRIMARY KEY,
        UserName VARCHAR(100)
    );
    
    CREATE TABLE Order (
        OrderID INT PRIMARY KEY,
        OrderDate DATE,
        UserID INT,
        FOREIGN KEY (UserID) REFERENCES User(UserID)
    );
    이 예제에서 Order 테이블의 UserID 열은 User 테이블의 UserID 열을 참조하는 외래키. 
    이를 통해 각 주문이 어떤 사용자에게 속하는지를 나타낼 수 있음.
     User 에 연관된 Order 들을 조회하는 방법
     
    JOIN 구문을 사용하여 User 테이블과 Order 테이블을 연결
        SELECT User.UserID, UserName, Order.OrderID, OrderDate
        FROM User
        JOIN Order ON User.UserID = Order.UserID
        WHERE User.UserID = (id값);
    SELECT 구문은 조회하고자 하는 컬럼들을 명시.
    여기서는 사용자의 ID와 이름, 그리고 주문의 ID와 날짜를 선택.
    FROM User는 조회의 기준이 되는 테이블을 명시.
    JOIN Order ON User.UserID = Order.UserID는
    User 테이블과 Order 테이블을 UserID를 기준으로 연결하여
    User 테이블의 UserID와 Order 테이블의 UserID가 일치하는 행들을 결합.
    WHERE User.UserID = ? 구문은 특정 사용자의 ID를 기준으로 필터링하여
    해당 사용자에 대한 주문 정보만 선택.
  • mappedBy 속성은 관계의 소유가 아닌 쪽에서 사용되며, 
    관계의 소유자 쪽에서 사용된 필드나 프로퍼티의 이름을 값을 씀. 
    mappedBy는 해당 관계에서 주체가 되는 쪽이 아닌, 참조를 위한 쪽에서 사용됨.
    
    User 엔티티에서 Order 리스트에 대한 참조는 user 쪽에서 mappedBy 속성을 사용하여 설정함. 
    
    public class User {
        @OneToMany(mappedBy = "user")
        private List<Order> orders = new ArrayList<>();
    }
    
    public class Order {
        @ManyToOne
        @JoinColumn(name = "userId")
        private User user;
    }
    
    위 코드에서 @OneToMany(mappedBy = "user")는 
    User 엔티티가 여러 개의 Order 엔티티를 가지고 있음을 나타내며
    mappedBy 속성의 값 "user"는 Order 엔티티 내의 User 엔티티를 참조하는 필드의 이름으로 설정. 
    
    Order 테이블 내의 외래 키(userId)를 사용하여 두 엔티티 간의 관계를 관리함.
  • 다대다(N:M): 여러 영속 객체가 서로 다수의 관계를 가지는 경우
  • 두 엔티티 간에 여러 인스턴스가 서로 다수의 관계를 맺을 수 있는 경우를 말함.
    예를 들어, "학생"과 "강좌" 엔티티가 있을 때 하나의 학생이 여러 강좌를 수강할 수 있고,
    하나의 강좌에 여러 학생이 등록될 수 있는 상황. 
    이런 관계를 직접적으로 데이터베이스에 표현하는 것은 매우 어렵기 때문에,
    일반적으로 연결 테이블(또는 조인 테이블)을 사용하여 
    다대다 관계를 두 개의 일대다(1:N) 관계로 분리하여 관리함.
    
    ManyToMany 관계를 직접 사용하지 않는 이유
    주로 데이터 모델의 복잡도, 성능, 그리고 관계에 대한 추가 정보 저장의 필요성에 관련이 있음.
    
    1. 추가 정보 저장
    ManyToMany 관계를 통해 연결된 두 엔티티 사이에 추가적인 정보를 저장해야 할 경우가 많음.
    예시로 학생과 수업 사이의 관계에서 각 학생이 해당 수업에 등록한 날짜 정보. 
    이러한 정보는 ManyToMany 를 사용하면 저장할 곳이 없음.
    따라서 연결 테이블을 별도의 엔티티로 만들어 두 OneToMany 관계로 나누어 관리.
    
    2. 성능 최적화
    ManyToMany 관계는 성능상의 이슈를 야기. 
    특히, 대규모 데이터셋에서는 연결 테이블을 통해 데이터를 조회하고 관리하는 과정에서
    성능 저하가 발생함.
    복잡한 쿼리 작성 시에도 ManyToMany 관계는 최적화하기 어려움.
    
    3. 더 명확한 데이터 모델
    데이터 모델이 더 명확해짐.
    관계의 양쪽에서 발생할 수 있는 변경 사항을 더 쉽게 추적하고 관리할 수 있음.
    비즈니스 로직이 복잡해질 수록 필수적임.
    
    4. 유연성
    연결 테이블을 별도의 엔티티로 만들면, 나중에 비즈니스 요구사항이 변경되어 관계에 추가 정보를 저장해야 할 때 더 유연하게 대응할 수 있습니다. ManyToMany 관계에서는 이러한 변경이 어려울 수 있습니다.
    
    JPA에서는 @ManyToMany 어노테이션을 사용하여 다대다 관계를 매핑할 수 있음.
    JPA가 자동으로 연결 테이블을 생성함.
    
    public class Student {
        @ManyToMany
        @JoinTable(
            name = "student_course", 
            joinColumns = @JoinColumn(name = "student_id"), 
            inverseJoinColumns = @JoinColumn(name = "course_id")
        )
        private Set<Course> courses = new HashSet<>();
    }
    
    public class Course {
        @ManyToMany(mappedBy = "courses")
        private Set<Student> students = new HashSet<>();
    }
    
    @JoinTable 어노테이션은 연결 테이블을 정의하며,
    joinColumns는 현재 엔티티(Student)를 참조하는 컬럼,
    inverseJoinColumns는 반대편 엔티티(Course)를 참조하는 컬럼을 지정.
    mappedBy 속성은 역방향 관계에서 소유자 측 엔티티의 필드 이름을 지정.

'[프로그래밍] JPA' 카테고리의 다른 글

[JPA] JPA와 cascade, orphanRemoval  (0) 2024.05.05
[JPA] JPA와 Column  (0) 2024.05.05
[JPA] JPA의 기본 키 생성 전략  (0) 2024.05.04
[JPA] Entity Mapping  (0) 2024.05.04
[JPA] JPA의 영속성 컨텍스트  (0) 2024.05.03