Что означает «обратная сторона ассоциации» в двунаправленной ассоциации JPA OneToMany / ManyToOne? - PullRequest
159 голосов
/ 06 апреля 2010

В этих примерах Справочник по JPL TopLink :

Пример 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 в том же документе написано, что:

если отношения двунаправленные, затем установите элемент mappedBy на обратная (не владеющая) сторона привязка к названию поля или собственность, которая владеет отношениями как показано в примере 1-60.

Однако, если я не ошибаюсь, похоже, что в примере mappedBy фактически указывается на стороне-владельце ассоциации, а не на стороне-не-владельце.

Так что мой вопрос в основном:

  1. Какая из сущностей является владельцем в двунаправленной ассоциации (один ко многим / много ко одному)? Как мы можем обозначить одну сторону в качестве владельца? Как мы можем назначить Многую сторону в качестве владельца?

  2. Что подразумевается под «обратной стороной ассоциации»? Как мы можем обозначить одну сторону как обратную? Как мы можем обозначить сторону Множество как обратную?

Ответы [ 5 ]

295 голосов
/ 06 апреля 2010

Чтобы понять это, вы должны сделать шаг назад. В ОО заказчику принадлежат заказы (заказы представляют собой список в объекте клиента). Не может быть заказа без клиента. Таким образом, клиент, кажется, является владельцем заказов.

Но в мире 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'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;
}

Это объясняет «внешний ключ» проектных работ «многие к одному». Существует второй подход, который использует другую таблицу для поддержания отношений. Это означает, что в нашем первом примере у вас есть три таблицы: одна с клиентами, одна с заказами и таблица с двумя столбцами с парами первичных ключей (customerPK, orderPK).

Этот подход является более гибким, чем приведенный выше (он может легко обрабатывать один-к-одному, многие-к-одному, один-ко-многим и даже многие-ко-многим). Цена такова

  • это немного медленнее (для поддержки другой таблицы и объединений используются три таблицы вместо двух),
  • Синтаксис объединения является более сложным (что может быть утомительно, если вам приходится вручную писать много запросов, например, когда вы пытаетесь что-то отладить)
  • это более подвержено ошибкам, потому что вы можете внезапно получить слишком много или слишком мало результатов, если что-то пойдет не так в коде, управляющем таблицей соединений.

Вот почему я редко рекомендую такой подход.

40 голосов
/ 03 июня 2013

Невероятно, но за 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;
}
34 голосов
/ 21 октября 2011

Объект, имеющий таблицу с внешним ключом в базе данных, является владельцем-владельцем, а другая таблица, на которую указывают, является обратным объектом.

13 голосов
/ 24 февраля 2014

Простые правила двунаправленных отношений:

1.Для двунаправленных отношений «многие-к-одному» сторона многих всегда является собственностью отношений. Пример: 1 комната имеет много человек (человеку принадлежит только одна комната) -> сторона, владеющая личностью

2.Для двусторонних отношений «один-к-одному» сторона-владелец соответствует стороне, содержащей соответствующий внешний ключ.

3. Для двунаправленных отношений «многие ко многим» любая сторона может быть стороной-владельцем.

Надежда может помочь вам.

3 голосов
/ 10 августа 2013

Для двух классов сущностей Customer и Order hibernate создаст две таблицы.

Возможные случаи:

  1. mappedBy не используется в Customer.java и Order.javaКласс затем ->

    На стороне клиента будет создана новая таблица [name = CUSTOMER_ORDER], в которой будут отображаться CUSTOMER_ID и ORDER_ID.Это первичные ключи таблиц клиентов и заказов.На стороне Заказа требуется дополнительный столбец для сохранения соответствующего сопоставления записи Customer_ID.

  2. mappedBy используется в Customer.java [как указано в формулировке проблемы]. Теперь дополнительная таблица [CUSTOMER_ORDER] не создается.В Order.java используется только один столбец в таблице заказов

  3. mappedby. Теперь hibernate будет создавать дополнительную таблицу. [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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...