В двунаправленной ассоциации 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 - класс заказа с generics
@Entity
public class Order implements Serializable {
...
@ManyToOne
@JoinColumn(name="CUST_ID", nullable=false)
public Customer getCustomer() {
return customer;
}
...
}
Мне кажется, что объект Customer
является владельцем ассоциации. Однако в объяснении атрибута mappedBy
в том же документе написано, что:
если отношение является двунаправленным, затем установите элемент mappedBy на обратная (не владеющая) сторона ассоциация с названием поля или собственности, которая владеет отношениями как показывает пример 1-60.
Однако, если я не ошибаюсь, это похоже на пример, mappedBy
фактически указан на стороне владельца ассоциации, а не стороне, не принадлежащей стороне.
Итак, мой вопрос в основном:
-
В двунаправленной (одна-ко-многим/много-к-одному) ассоциации, какая из компаний является владельцем? Как мы можем обозначить одну сторону как владельца? Как мы можем назначить много сторон как владельца?
-
Что означает "обратная сторона ассоциации"? Как мы можем обозначить одну сторону как обратную? Как мы можем обозначить большую сторону как обратную?
Ответы
Ответ 1
Чтобы понять это, вы должны сделать шаг назад. В ОО заказчику принадлежат заказы (заказы представляют собой список в объекте клиента). Не может быть заказа без клиента. Таким образом, клиент, кажется, является владельцем заказов.
Но в мире SQL один элемент будет фактически содержать указатель на другой. Поскольку для N заказов есть 1 клиент, каждый заказ содержит внешний ключ к клиенту, которому он принадлежит. Это "соединение", и это означает, что заказ "владеет" (или буквально содержит) соединение (информация). Это как раз противоположность мира ОО/модели.
Это может помочь понять:
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;
}
Обратная сторона является ОО "владельцем" объекта, в данном случае клиентом. У клиента нет столбцов в таблице для хранения заказов, поэтому вы должны указать ему, где в таблице заказов он может сохранить эти данные (что происходит через 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 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;
}
Это объясняет "внешний ключ" проектных работ "многие к одному". Существует второй подход, который использует другую таблицу для поддержания отношений. Это означает, что в нашем первом примере у вас есть три таблицы: одна с клиентами, одна с заказами и таблица с двумя столбцами с парами первичных ключей (customerPK, orderPK).
Этот подход является более гибким, чем описанный выше (он может легко обрабатывать один-к-одному, многие-к-одному, один-ко-многим и даже многие-ко-многим). Цена такая
- это немного медленнее (для поддержки другой таблицы и объединений используются три таблицы вместо двух),
- синтаксис соединения более сложен (что может быть утомительно, если вам приходится вручную писать много запросов, например, когда вы пытаетесь что-то отладить)
- это более подвержено ошибкам, потому что вы можете внезапно получить слишком много или слишком мало результатов, если что-то пойдет не так в коде, который управляет таблицей соединений.
Вот почему я редко рекомендую такой подход.
Ответ 2
Невероятно, через 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 ожидает двойное сопоставление (в базе данных будет как столбец соединения, так и таблица соединений, что является избыточным (обычно нежелательным)).
// "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;
}
Ответ 3
Объект, который имеет таблицу с внешним ключом в базе данных, является объектом-владельцем, а другая таблица, на которую указана точка, является обратной сущностью.
Ответ 4
Простые правила двунаправленных отношений:
1. Для двунаправленных отношений "один-к-одному" многие стороны всегда являются собственностью стороны отношений. Пример: 1 номер имеет много Лица (человек принадлежит только один номер) → владеющая сторона - это человек
2. Для двусторонних двунаправленных отношений сторона-обладательница соответствует стороне, которая содержит соответствующий внешний ключ.
3. Для двунаправленных отношений "многие-ко-многим" любая сторона может быть стороной-владельцем.
Надежда может вам помочь.
Ответ 5
Для двух классов сущностей Customer и Order, hibernate создаст две таблицы.
Возможные случаи:
-
mappedBy не используется в Customer.java и Order.java Class then →
Со стороны клиента будет создана новая таблица [name = CUSTOMER_ORDER], которая будет сохранять сопоставление CUSTOMER_ID и ORDER_ID. Это первичные ключи таблиц клиентов и заказов.
На стороне заказа требуется дополнительный столбец для сохранения соответствующего сопоставления записей Customer_ID.
-
mappedBy используется в Customer.java [Как указано в описании проблемы]
Теперь не создается дополнительная таблица [CUSTOMER_ORDER]. Только один столбец в таблице заказов
-
mappedby используется в 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();
}
}