양방향 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
실제로는 비 소유 측이 아닌 협회의 소유 측에 지정되어 있습니다.
그래서 내 질문은 기본적으로 :
양방향 (일대 다 / 다 대일) 연결에서 소유자 중 어느 엔터티입니까? 한면을 소유자로 어떻게 지정할 수 있습니까? 다수를 소유자로 어떻게 지정할 수 있습니까?
"협회의 반대면"이란 무엇입니까? 우리는 어떻게 한쪽을 역으로 지정할 수 있습니까? 우리는 어떻게 많은면을 역수로 지정할 수 있습니까?
이를 이해하려면 한발 물러서야합니다. 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의 경우 최대 절전 모드는 두 개의 테이블을 작성합니다.
가능한 경우 :
mappingBy는 Customer.java 및 Order.java 클래스에서 사용되지 않습니다.
고객 측에서 CUSTOMER_ID와 ORDER_ID의 매핑을 유지하는 새 테이블이 생성됩니다 [이름 = CUSTOMER_ORDER]. 이들은 고객 및 주문 테이블의 기본 키입니다. 주문 측에서는 해당 Customer_ID 레코드 매핑을 저장하기 위해 추가 열이 필요합니다.
mappingBy는 Customer.java에서 사용됩니다. [문제 설명에 제공된대로] 이제 추가 테이블 [CUSTOMER_ORDER]이 작성되지 않습니다. 주문 테이블에서 하나의 열만
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();
}
}
'Programing' 카테고리의 다른 글
Postgres 배열에 값이 있는지 확인 (0) | 2020.06.02 |
---|---|
슬라이스 방법이 들어 있습니다 (0) | 2020.06.02 |
Razor View Engine : 표현식 트리에 동적 작업이 포함되어 있지 않을 수 있습니다 (0) | 2020.06.02 |
Nachos 소스 코드를 컴파일하는 동안 오류 "gnu / stubs-32.h : 해당 파일 또는 디렉토리가 없습니다" (0) | 2020.06.01 |
해시에 새 항목을 추가하는 방법 (0) | 2020.06.01 |