Programing

양방향 JPA OneToMany / ManyToOne 연결에서“연결의 반대면”이란 무엇입니까?

crosscheck 2020. 6. 2. 22:12
반응형

양방향 JPA OneToMany / ManyToOne 연결에서“연결의 반대면”이란 무엇입니까?


TopLink JPA 주석 참조 에 대한이 예제에서 :

예 1-59 @OneToMany-제네릭이있는 고객 클래스

@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동일한 문서 속성에 대한 설명 에서 다음과 같이 작성됩니다.

관계가 양방향 인 경우, 연관의 비 (소유하지 않은)면에서 mappingBy 요소를 예 1-60에 표시된대로 관계를 소유하는 필드 또는 특성의 이름으로 설정하십시오.

그러나 내가 틀리지 않으면 예와 같이 보입니다. mappedBy실제로는 비 소유 측이 아닌 협회의 소유 측에 지정되어 있습니다.

그래서 내 질문은 기본적으로 :

  1. 양방향 (일대 다 / 다 대일) 연결에서 소유자 중 어느 엔터티입니까? 한면을 소유자로 어떻게 지정할 수 있습니까? 다수를 소유자로 어떻게 지정할 수 있습니까?

  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;
}

이것은 "외국 키"다 대일 디자인 작업에 대해 설명합니다. 관계를 유지하기 위해 다른 테이블을 사용하는 두 번째 방법이 있습니다. 즉, 첫 번째 예의 경우 고객이있는 테이블과 주문이있는 테이블, 기본 키 쌍이있는 2 열 테이블 (customerPK, orderPK)의 세 가지 테이블이 있습니다.

이 방법은 위의 방법보다 융통성이 있습니다 (일대일, 다 대일, 일대 다 및 다대 다를 쉽게 처리 할 수 ​​있음). 가격은

  • 조금 느립니다 (다른 테이블을 유지하고 조인하면 두 테이블 대신 세 테이블을 사용합니다).
  • 조인 구문은 더 복잡합니다 (예를 들어 무언가를 디버깅하려고 할 때 많은 쿼리를 수동으로 작성해야하는 경우 지루할 수 있음)
  • 연결 테이블을 관리하는 코드에서 무언가 잘못되었을 때 갑자기 너무 많거나 너무 적은 결과를 얻을 수 있기 때문에 오류가 발생하기 쉽습니다.

그렇기 때문에이 방법을 거의 권장하지 않습니다.


믿을 수 없을 정도로, 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
}

유일한 양방향 매핑 솔루션은 "many"쪽이 "one"에 대한 포인터를 소유하고 @OneToMany "mappedBy"속성을 사용하는 것입니다. "mappedBy"속성이 없으면 Hibernate는 이중 맵핑을 예상 할 것이다 (데이터베이스는 조인 컬럼과 조인 테이블을 둘 다 가질 것이다.

// "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. 일대일 양방향 관계의 경우, 소유 측은 해당 외래 키를 포함하는 측에 해당합니다.

다 대다 양방향 관계의 경우, 어느 쪽이든 소유하는 쪽일 수있다.

희망이 당신을 도울 수 있습니다.


두 개의 엔티티 클래스 Customer 및 Order의 경우 최대 절전 모드는 두 개의 테이블을 작성합니다.

가능한 경우 :

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

    고객 측에서 CUSTOMER_ID와 ORDER_ID의 매핑을 유지하는 새 테이블이 생성됩니다 [이름 = CUSTOMER_ORDER]. 이들은 고객 및 주문 테이블의 기본 키입니다. 주문 측에서는 해당 Customer_ID 레코드 매핑을 저장하기 위해 추가 열이 필요합니다.

  2. mappingBy는 Customer.java에서 사용됩니다. [문제 설명에 제공된대로] 이제 추가 테이블 [CUSTOMER_ORDER]이 작성되지 않습니다. 주문 테이블에서 하나의 열만

  3. mappingby가 Order.java에서 사용됩니다. 이제 추가 테이블이 최대 절전 모드로 작성됩니다. [name = 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();
    }
}

참고 URL : https://stackoverflow.com/questions/2584521/in-a-bidirectional-jpa-onetomany-manytoone-association-what-is-meant-by-the-in

반응형