双向 JPA OneToMany/ManyToOne 关联中的“关联的反面”是什么?

新手上路,请多包涵

@OneToMany 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 属性的解释中,写道:

如果关系是双向的,则将关联的反向(非拥有)端的 mappedBy 元素设置为拥有该关系的字段或属性的名称,如示例 1-60 所示。

但是,如果我没记错的话,示例中的 mappedBy 实际上是在关联的拥有方而不是非拥有方指定的。

所以我的问题基本上是:

  1. 在双向(一对多/多对一)关联中,哪个实体是所有者?我们如何指定一方为拥有者?我们如何指定多方为所有者?

  2. “协会的反面”是什么意思?我们怎么能把单边指定为反面呢?我们如何将多面指定为逆面?

原文由 Behrang 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 764
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;
}

这解释了为“外键”设计的多对一工作原理。还有第二种方法,它使用另一个表来维护关系。这意味着,对于我们的第一个示例,您有三个表:一个包含客户的表,一个包含订单的表和一个包含主键对(customerPK、orderPK)的两列表。

这种方式比上面的方式更灵活(可以轻松处理一对一、多对一、一对多甚至多对多)。价格是

  • 它有点慢(必须维护另一个表并连接使用三个表而不是两个),
  • 连接语法更复杂(如果您必须手动编写许多查询,例如当您尝试调试某些东西时,这可能会很乏味)
  • 它更容易出错,因为当管理连接表的代码出现问题时,您可能会突然得到太多或太少的结果。

这就是为什么我很少推荐这种方法。

原文由 Aaron Digulla 发布,翻译遵循 CC BY-SA 4.0 许可协议

令人难以置信的是,在 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;
}

原文由 Steve Jones 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题