在双向JPA OneToMany/ManyToOne关联中,"关联的反面"是什么意思?

Lam*_*bda 160 java hibernate jpa

TopLink JPA Annotation Reference的这些示例中:

例1-59 @OneToMany - 使用泛型的客户类

@Entity
public class Customer implements Serializable {
    ...
    @OneToMany(cascade=ALL, mappedBy="customer")
    public Set<Order> getOrders() { 
        return orders; 
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

例1-60 @ManyToOne - 带有泛型的订单类

@Entity
public class Order implements Serializable {
    ...
    @ManyToOne
    @JoinColumn(name="CUST_ID", nullable=false)
    public Customer getCustomer() { 
        return customer; 
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

在我看来,该Customer实体是该协会的所有者.但是,在mappedBy同一文档中对属性的解释中,写成:

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

但是,如果我没有错,看起来在示例中,mappedBy实际上是在关联的拥有方指定,而不是非拥有方.

所以我的问题基本上是:

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

  2. 什么是"关联的反面"?我们怎样才能将一面指定为反面?我们如何将多边指定为反面?

Aar*_*lla 296

要理解这一点,你必须退后一步.在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;
}
Run Code Online (Sandbox Code Playgroud)

反面是对象的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;
}
Run Code Online (Sandbox Code Playgroud)

这解释了"外键"多对一设计的作用.还有第二种方法使用另一个表来维持关系.这意味着,对于我们的第一个示例,您有三个表:一个包含客户,一个包含订单,另一个包含两对主表(customerPK,orderPK).

这种方法比上面的方法更灵活(它可以轻松处理一对一,多对一,一对多甚至多对多).价格是这样的

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

这就是我很少推荐这种方法的原因.

  • 只是为了澄清:很多方是主人; 一方是反面的.你没有选择(实际上). (35认同)
  • 不,Hibernate发明了这个.我不喜欢它,因为它将部分实现暴露给OO模型.我更喜欢使用`@ Parent`或`@ Child`注释而不是"XtoY"来说明连接*的含义*(而不是它是如何*实现的*) (10认同)
  • 哇.如果只有ORM框架文档有这么好的解释 - 这将使整个事情更容易吞下!很好的答案! (6认同)
  • @AaronDigulla每次我都要通过OneToMany映射我来看这个答案,可能是关于SO的最佳主题. (4认同)
  • @klausch:Hibernate文档令人困惑.忽略它.查看代码,数据库中的SQL以及外键的工作方式.如果你愿意,你可以把智慧放在家里:文档是谎言.使用来源,卢克. (2认同)

小智 40

令人难以置信的是,在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
}
Run Code Online (Sandbox Code Playgroud)

唯一的双向映射解决方案是让"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;
}
Run Code Online (Sandbox Code Playgroud)

  • 在您的单向示例中,JPA 期望存在一个额外的 customer_orders 表。使用 JPA2,您可以在 Customer 的 orders 字段上使用 @JoinColumn 注释(我似乎经常使用)来表示应该使用的 Order 表中的数据库外键列。这样,您在 Java 中就有了单向关系,同时在 Order 表中仍然有一个外键列。所以在对象世界中,订单不知道客户,而在数据库世界中,客户不知道订单。 (2认同)

小智 34

在数据库中具有带有外键的表的实体是拥有实体,而指向的另一个表是反向实体.

  • 甚至更简单:所有者是带有FK列的表 (28认同)
  • 简单而好的解释.任何一方都可以成为所有者.如果我们在Order.java中使用mappedBy,在Customer字段<从Customer.java中删除mappedby>,那么将创建一个类似Order_Customer的新表,它将有2列.ORDER_ID和CUSTOMER_ID. (2认同)

Ken*_*ock 13

简单的双向关系规则:

对于多对一的双向关系,许多方面始终是关系的拥有方.示例:1个房间有很多人(一个人只属于一个房间) - >拥有方是人

2.对于一对一的双向关系,拥有方对应于包含相应外键的一侧.

3.对于多对多双向关系,任何一方都可能是拥有方.

希望可以帮到你.