programing

양방향 JPA OneToMany/ManyToOne 어소시에이션의 '어소시에이션의 역방향'이란 무엇입니까?

prostudy 2022. 8. 30. 21:24
반응형

양방향 JPA OneToMany/ManyToOne 어소시에이션의 '어소시에이션의 역방향'이란 무엇입니까?

JPA 주석 참조의 예제 섹션에서 다음을 수행합니다.

예 1-59 @OneToMany - Generic을 사용하는 고객 클래스

@Entity
public class Customer implements Serializable {
    ...
    @OneToMany(cascade=ALL, mappedBy="customer")
    public Set<Order> getOrders() { 
        return orders; 
    }
    ...
}

예 1-60 @ManyToOne - 제네릭스를 사용한 주문 클래스

@Entity
public class Order implements Serializable {
    ...
    @ManyToOne
    @JoinColumn(name="CUST_ID", nullable=false)
    public Customer getCustomer() { 
        return customer; 
    }
    ...
}

엔 ...인 것 .Customer입니다. '의에서는 ''의mappedBy같은 문서의 속성에는 다음과 같이 기재되어 있습니다.

관계가 쌍방향일 경우 예 1-60과 같이 관련지어 반전(비소유) 측에 있는 mappedBy 요소를 관계를 소유하는 필드 또는 속성의 이름으로 설정합니다.

제가.mappedBy는, 실제로는, 어소시에이션의 소유측에서 지정됩니다만, 비패킷측이 아닙니다.

제 질문은 기본적으로 다음과 같습니다.

  1. 쌍방향(1 대 다 / 다 대 1) 어소시에이션에서는 어느 엔티티가 오너입니까?어떻게 하면 한쪽을 소유자로 지정할 수 있을까요?Many side를 어떻게 소유자로 지정할 수 있습니까?

  2. '연상의 반대편'은 무슨 뜻입니까?일변은 어떻게 역방향으로 지정할 수 있을까요?다면체를 어떻게 역방향으로 지정할 수 있을까요?

이를 이해하려면 한 걸음 물러서야 합니다.OO에서는 고객이 주문을 소유합니다(주문은 고객 객체의 목록입니다).손님이 없으면 주문이 있을 수 없다.그래서 고객이 주문의 주인인 것 같습니다.

그러나 SQL 세계에서는 실제로 한 항목에 다른 항목에 대한 포인터가 포함됩니다.N개의 주문에 대해 1명의 고객이 있기 때문에 각 주문에는 해당 고객에 대한 외부 키가 포함되어 있습니다.이것은 "연결"이며, 이는 연결(정보)을 "소유"(또는 문자 그대로 포함)하는 주문을 의미합니다.이것은 OO/모델의 세계와는 정반대입니다.

이것은 다음 사항을 이해하는 데 도움이 될 수 있습니다.

public class Customer {
     // This field doesn't exist in the database
     // It is simulated with a SQL query
     // "OO speak": Customer owns the orders
     private List<Order> orders;
}

public class Order {
     // This field actually exists in the DB
     // In a purely OO model, we could omit it
     // "DB speak": Order contains a foreign key to customer
     private Customer customer;
}

"OO " "소소소소이이이이이이이이이이이이이이이이이 " 。할 수 있는 .따라서 주문 테이블에서 이 데이터를 저장할 수 있는 위치를 알려줘야 합니다.mappedBy를 참조해 주세요.

또 다른 일반적인 예는 부모 및 자녀 모두일 수 있는 노드를 가진 트리입니다.이 경우 다음 두 개의 필드가 하나의 클래스에서 사용됩니다.

public class Node {
    // Again, this is managed by Hibernate.
    // There is no matching column in the database.
    @OneToMany(cascade = CascadeType.ALL) // mappedBy is only necessary when there are two fields with the type "Node"
    private List<Node> children;

    // This field exists in the database.
    // For the OO model, it's not really necessary and in fact
    // some XML implementations omit it to save memory.
    // Of course, that limits your options to navigate the tree.
    @ManyToOne
    private Node parent;
}

이것이, 「외부 키」의 다대일 설계 작업에 대한 설명입니다.관계를 유지하기 위해 다른 표를 사용하는 두 번째 방법이 있습니다.즉, 첫 번째 예에서는 3개의 테이블이 있습니다.고객이 있는 것, 주문이 있는 것, 프라이머리 키 쌍이 있는 2열 표(customer PK, order PK).

이 접근방식은 위의 접근방식보다 유연합니다(1대1, 다대1, 1대다, 다대다, 심지어 다대다도 쉽게 처리할 수 있습니다).대가는 이다

  • 조금 느립니다(다른 테이블을 유지해야 하고 조인에는 2개의 테이블이 아닌 3개의 테이블을 사용합니다).
  • join 구문은 더 복잡합니다(예를 들어 무언가를 디버깅하려고 할 때 등 많은 쿼리를 수동으로 써야 하는 경우 지루할 수 있습니다).
  • 접속 테이블을 관리하는 코드에 문제가 생겼을 때 갑자기 너무 많거나 적은 결과를 얻을 수 있기 때문에 오류가 발생하기 쉽습니다.

그래서 저는 이 방법을 거의 추천하지 않습니다.

놀랍게도, 3년 동안 아무도 당신의 훌륭한 질문에 대해 두 가지 관계를 매핑하는 예를 들어 대답하지 않았습니다.

다른 사용자가 언급했듯이 "소유자" 측에는 포인터(외부 키)가 데이터베이스에 포함되어 있습니다.어느 한쪽을 소유자로 지정할 수도 있지만, 한쪽을 소유자로 지정하면 관계가 양방향으로 되지 않습니다(역방향으로 "다수"라고 하는 역방향은 "소유자"를 인식하지 않습니다).이것은 캡슐화/루즈 커플링에 바람직할 수 있습니다.

// "One" Customer owns the associated orders by storing them in a customer_orders join table
public class Customer {
    @OneToMany(cascade = CascadeType.ALL)
    private List<Order> orders;
}

// if the Customer owns the orders using the customer_orders table,
// Order has no knowledge of its Customer
public class Order {
    // @ManyToOne annotation has no "mappedBy" attribute to link bidirectionally
}

유일한 양방향 매핑솔루션은 "다수"측에서 "하나"에 대한 포인터를 소유하고 @OneToMany "mappedBy" 속성을 사용하는 것입니다."mappedBy" 속성이 없으면 Hibernate는 이중 매핑을 예상합니다(데이터베이스에 join column과 join 테이블이 모두 포함되며, 이는 장황합니다(일반적으로 바람직하지 않습니다).

// "One" Customer as the inverse side of the relationship
public class Customer {
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "customer")
    private List<Order> orders;
}

// "many" orders each own their pointer to a Customer
public class Order {
    @ManyToOne
    private Customer customer;
}

데이터베이스에 외부 키가 있는 테이블이 있는 엔티티는 소유 엔티티이고 다른 테이블은 가리키는 엔티티입니다.

쌍방향 관계의 간단한 규칙:

1. 다대일 쌍방향 관계에서는 다방이 항상 관계의 소유측입니다.예: 1개의 방에 여러 명의 사람이 있다(한 사람이 한 개의 방에만 속해 있다) -> 소유하는 쪽이 사람이다.

2. 일대일 쌍방향 관계에서는 소유측이 대응하는 외부키를 포함한 측에 대응한다.

3. 다대다 쌍방향 관계에서는 어느 한쪽이 소유자가 될 수 있습니다.

호프가 도와줄 수 있어

[ Customer ] order [ Order ]의 2개의 엔티티 클래스의 경우 [hibernate]에 의해 2개의 테이블이 생성됩니다.

생각할 수 있는 케이스:

  1. mappedBy는 Customer.java 및 Order.java 클래스에서 사용되지 않습니다.->

    고객 측에서 [이름 = CUSTOMER_ORDER]가 생성되어 CUSTOMER_ID와 ORDER_ID의 매핑이 유지됩니다.고객 및 주문표의 주요 키입니다.주문측에서 대응하는 고객을 저장하려면 추가 컬럼이 필요합니다.ID 레코드 매핑

  2. customer.java [문제의 설명과 같이]에서 mappedBy가 사용되고 있습니다.추가 테이블 [CUSTOMER_ORDER]은 작성되지 않습니다.주문 테이블에 열이 하나만 있음

  3. mappby는 Order.java에서 사용됩니다.이제 휴지 상태에 의해 추가 테이블이 생성됩니다.[이름 = CUSTOMER_ORDER] 주문 테이블에 추가 열이 없습니다 [Customer_매핑하기 위한 ID >.

어느 쪽이든 관계의 소유자가 될 수 있습니다.단, xxxToOne을 선택하는 것이 좋습니다.

코딩 효과 -> 엔티티의 소유측만 관계 상태를 변경할 수 있습니다.다음 예제에서는 BoyFriend 클래스가 관계의 소유자입니다.여자친구가 헤어지고 싶어도 헤어질 수 없어요.

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "BoyFriend21")
public class BoyFriend21 {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Boy_ID")
    @SequenceGenerator(name = "Boy_ID", sequenceName = "Boy_ID_SEQUENCER", initialValue = 10,allocationSize = 1)
    private Integer id;

    @Column(name = "BOY_NAME")
    private String name;

    @OneToOne(cascade = { CascadeType.ALL })
    private GirlFriend21 girlFriend;

    public BoyFriend21(String name) {
        this.name = name;
    }

    public BoyFriend21() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BoyFriend21(String name, GirlFriend21 girlFriend) {
        this.name = name;
        this.girlFriend = girlFriend;
    }

    public GirlFriend21 getGirlFriend() {
        return girlFriend;
    }

    public void setGirlFriend(GirlFriend21 girlFriend) {
        this.girlFriend = girlFriend;
    }
}

import org.hibernate.annotations.*;
import javax.persistence.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.List;

@Entity 
@Table(name = "GirlFriend21")
public class GirlFriend21 {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Girl_ID")
    @SequenceGenerator(name = "Girl_ID", sequenceName = "Girl_ID_SEQUENCER", initialValue = 10,allocationSize = 1)
    private Integer id;

    @Column(name = "GIRL_NAME")
    private String name;

    @OneToOne(cascade = {CascadeType.ALL},mappedBy = "girlFriend")
    private BoyFriend21 boyFriends = new BoyFriend21();

    public GirlFriend21() {
    }

    public GirlFriend21(String name) {
        this.name = name;
    }


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public GirlFriend21(String name, BoyFriend21 boyFriends) {
        this.name = name;
        this.boyFriends = boyFriends;
    }

    public BoyFriend21 getBoyFriends() {
        return boyFriends;
    }

    public void setBoyFriends(BoyFriend21 boyFriends) {
        this.boyFriends = boyFriends;
    }
}


import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import java.util.Arrays;

public class Main578_DS {

    public static void main(String[] args) {
        final Configuration configuration = new Configuration();
         try {
             configuration.configure("hibernate.cfg.xml");
         } catch (HibernateException e) {
             throw new RuntimeException(e);
         }
        final SessionFactory sessionFactory = configuration.buildSessionFactory();
        final Session session = sessionFactory.openSession();
        session.beginTransaction();

        final BoyFriend21 clinton = new BoyFriend21("Bill Clinton");
        final GirlFriend21 monica = new GirlFriend21("monica lewinsky");

        clinton.setGirlFriend(monica);
        session.save(clinton);

        session.getTransaction().commit();
        session.close();
    }
}

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import java.util.List;

public class Main578_Modify {

    public static void main(String[] args) {
        final Configuration configuration = new Configuration();
        try {
            configuration.configure("hibernate.cfg.xml");
        } catch (HibernateException e) {
            throw new RuntimeException(e);
        }
        final SessionFactory sessionFactory = configuration.buildSessionFactory();
        final Session session1 = sessionFactory.openSession();
        session1.beginTransaction();

        GirlFriend21 monica = (GirlFriend21)session1.load(GirlFriend21.class,10);  // Monica lewinsky record has id  10.
        BoyFriend21 boyfriend = monica.getBoyFriends();
        System.out.println(boyfriend.getName()); // It will print  Clinton Name
        monica.setBoyFriends(null); // It will not impact relationship

        session1.getTransaction().commit();
        session1.close();

        final Session session2 = sessionFactory.openSession();
        session2.beginTransaction();

        BoyFriend21 clinton = (BoyFriend21)session2.load(BoyFriend21.class,10);  // Bill clinton record

        GirlFriend21 girlfriend = clinton.getGirlFriend();
        System.out.println(girlfriend.getName()); // It will print Monica name.
        //But if Clinton[Who owns the relationship as per "mappedby" rule can break this]
        clinton.setGirlFriend(null);
        // Now if Monica tries to check BoyFriend Details, she will find Clinton is no more her boyFriend
        session2.getTransaction().commit();
        session2.close();

        final Session session3 = sessionFactory.openSession();
        session1.beginTransaction();

        monica = (GirlFriend21)session3.load(GirlFriend21.class,10);  // Monica lewinsky record has id  10.
        boyfriend = monica.getBoyFriends();

        System.out.println(boyfriend.getName()); // Does not print Clinton Name

        session3.getTransaction().commit();
        session3.close();
    }
}

테이블 관계와 엔티티 관계

릴레이셔널 데이터베이스 시스템에서는 테이블 관계에는 다음 세 가지 유형만 사용할 수 있습니다.

  • 1 대 다(외부 키 열 사용)
  • 1 대 1 (공유 프라이머리 키 경유)
  • mody-to-many (2개의 외부 키가 있는 링크테이블을 통해 2개의 개별 부모 테이블을 참조)

상비 so so so soone-to-many테이블 관계는 다음과 같습니다.

일대다 테이블 관계

는 []컬럼 [Foreign Key])에.★★★★★★★★★★★★★★★★★★★post_id를 참조해 주세요를 참조해 주세요.

.one-to-many테이블 관계

「」에 되고 엔티티 , 「」는 「2」입니다.one-to-many"CHANGE: "CHANGE: " 。

쌍방향 1대 다 엔티티 어소시에이션

위의 그림을 보면 이 관계를 관리하는 방법은 두 가지가 있음을 알 수 있습니다.

서서 Post엔티티, 「」가 .comments수집:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

그리고...PostComment,그post어소시에이션은 다음과 같이 매핑됩니다.

@ManyToOne(
    fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;

따라서 엔티티 어소시에이션을 변경할 수 있는2개의 면이 있습니다.

  • 에 엔트리를 추가함으로써comments아이 컬렉션, 새로운post_comment행은 부모에 관련지어야 합니다.post을 통한 실체post_id기둥.
  • 를 설정함으로써post의 특성PostComment엔티티,post_id컬럼도 갱신해야 합니다.

[외부 키(Foreign Key)]컬럼을 표시하는 방법에는 2가지가 있기 때문에 어소시에이션스테이트 변경을 동등한 [외부 키(Foreign Key)]컬럼 값 변경으로 변환할 때 어느 것이 진실의 소스인지 정의해야 합니다.

MappedBy(역측이라고도 함)

mappedByAtribute가 나타내는 것은@ManyToOneside는 Foreign Key 컬럼 관리를 담당하며 컬렉션은 하위 엔티티를 가져오고 상위 엔티티 상태 변경을 하위 엔티티에 계단식으로 전달하는 데만 사용됩니다(예를 들어, 상위 엔티티를 제거하면 하위 엔티티도 제거됩니다).

이 테이블 관계를 관리하는 하위 엔티티 속성을 참조하기 때문에 역변이라고 합니다.

쌍방향 어소시에이션의 양쪽을 동기화합니다.

자, 이제, 만약 여러분이mappedByAtribute 및 자녀측@ManyToOneassociation은 [Foreign Key]컬럼을 관리하지만 쌍방향 어소시에이션의 양쪽을 동기화할 필요가 있습니다.

가장 좋은 방법은 다음 두 가지 유틸리티 방법을 추가하는 것입니다.

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

및 메서드에 의해 양쪽이 동기화됩니다.따라서 하위 엔티티를 추가할 경우 하위 엔티티는 상위 엔티티를 가리키고 상위 엔티티는 하위 컬렉션에 하위 엔티티를 포함해야 합니다.

언급URL : https://stackoverflow.com/questions/2584521/what-is-the-inverse-side-of-the-association-in-a-bidirectional-jpa-onetomany-m

반응형