使用JPA @OneToMany关联时@JoinColumn和mappedBy之间有什么区别

Myk*_*ych 483 java orm hibernate jpa one-to-many

有什么区别:

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
    private List<Branch> branches;
    ...
}
Run Code Online (Sandbox Code Playgroud)

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy = "companyIdRef")
    private List<Branch> branches;
    ...
}
Run Code Online (Sandbox Code Playgroud)

Ósc*_*pez 522

注释@JoinColumn指示此实体是关系的所有者(即:对应的表具有带引用表的外键的列),而该属性mappedBy指示此方中的实体是关系的反转,并且所有者居住在"其他"实体中.这也意味着您可以从使用"mappedBy"注释的类中访问另一个表(完全双向关系).

特别是,对于问题中的代码,正确的注释将如下所示:

@Entity
public class Company {
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
    private List<Branch> branches;
}

@Entity
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyId")
    private Company company;
}
Run Code Online (Sandbox Code Playgroud)

  • @MykhayloAdamovych我用示例代码更新了我的答案.请注意,在`Company`中使用`@ JoinColumn`是错误的 (12认同)
  • @MykhayloAdamovych:不,那实际上并不是很正确.如果`Branch`没有引用`Company`的属性,但是底层表有一个列,那么你可以使用`@ JoinTable`来映射它.这是一种不寻常的情况,因为您通常会在对应于其表的对象中映射列,但它可能会发生,并且它是完全合法的. (10认同)
  • 在这两种情况下,Branch都有具有公司ID的字段. (3认同)
  • 公司表没有一个带有引用表的外键的列 - Branch已经引用了公司..为什么你说"相应的表有一个带有引用表的外键的列"?你能解释一下吗? (3认同)
  • 这是不喜欢ORM的另一个原因.文档往往太狡猾,在我的书中,这是在太多神奇的领域蜿蜒曲折.我一直在努力解决这个问题,当一个字一个接一个地跟着`@ OneToOne`时,子行在他们引用父代的FKey列中用`null`更新. (3认同)
  • 对于带有 @JoinColumn 的单向 @OneToMany,其中 fk 位于子实体中,“注释 @JoinColumn 指示该实体是关系的所有者”为 **false**。示例:https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ (2认同)

Myk*_*ych 213

@JoinColumn可以用于双方的关系.现在的问题是关于使用@JoinColumn@OneToMany侧(极少数情况下).这里的重点是物理信息重复(列名)以及未优化的SQL查询,它将产生一些额外的UPDATE语句.

根据文件:

由于多个到一个(几乎)总是JPA规范中双向关系的所有者方,因此一对多关联由@OneToMany注释(mappedBy = ...)

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
} 
Run Code Online (Sandbox Code Playgroud)

部队通过部队属性与士兵有一对一的双向关系.您不必(必须)在mappedBy端定义任何物理映射.

要将双向一个映射到多个,以一对多方作为拥有方,您必须删除mappedBy元素并将多个@JoinColumn设置为可插入且可更新为false.此解决方案未经过优化,将生成一些额外的UPDATE语句.

@Entity
public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk", insertable=false, updatable=false)
    public Troop getTroop() {
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 在你的例子中,注释`mappedBy ="troop"`指的是哪个字段? (8认同)
  • 我无法弄清楚部队如何成为第二个片段中的所有者,士兵仍然是所有者,因为它包含引用部队的外键。(我正在使用mysql,我检查了你的方法)。 (4认同)
  • @Fractaliste注释`mappedBy ="troop"`指的是士兵类中的属性部队.在上面的代码中,属性是不可见的,因为Mykhaylo在这里省略了它,但你可以通过getter getTroop()来推断它的存在.检查[ÓscarLópez](http://stackoverflow.com/a/11938290/589914)的答案,非常明确,你会明白这一点. (4认同)
  • 这个例子是对 JPA 2 规范的滥用。如果作者的目标是创建双向关系,那么它应该在父端使用mappedBy,在子端使用JoinColumn(如果需要)。通过这里介绍的方法,我们得到了 2 个单向关系:OneToMany 和 ManyToOne,它们是独立的,但幸运的是(更多是误用)这两个关系是使用相同的外键定义的 (3认同)

Vla*_*cea 47

正如我在本文中所解释的,如果您使用@OneToMany注释@JoinColumn,那么您将具有单向关联.

如果使用@OneToManywith mappedBy属性集,则表示您具有双向关联,这意味着您需要@ManyToOne在子对象上具有mappedBy引用的关联.

单向@OneToMany协会不执行得非常好,所以你应该避免.

你最好使用更高效双向@OneToMany.


Cor*_*ral 32

理想情况下,注释mappedBy应始终用于双向关系的Parent方(Company类),在这种情况下,它应该在Company类中指向Child类的成员变量'company'(Branch class)

注释@JoinColumn用于指定用于连接实体关联的映射列,此注释可以在任何类(父或子)中使用,但理想情况下应该只在一侧使用(在父类或子类中不使用)在这两种情况下,在这种情况下,我在双向关系的Child侧(Branch类)中使用它来指示Branch类中的外键.

下面是工作示例:

父母班,公司

@Entity
public class Company {


    private int companyId;
    private String companyName;
    private List<Branch> branches;

    @Id
    @GeneratedValue
    @Column(name="COMPANY_ID")
    public int getCompanyId() {
        return companyId;
    }

    public void setCompanyId(int companyId) {
        this.companyId = companyId;
    }

    @Column(name="COMPANY_NAME")
    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
    public List<Branch> getBranches() {
        return branches;
    }

    public void setBranches(List<Branch> branches) {
        this.branches = branches;
    }


}
Run Code Online (Sandbox Code Playgroud)

儿童班,科

@Entity
public class Branch {

    private int branchId;
    private String branchName;
    private Company company;

    @Id
    @GeneratedValue
    @Column(name="BRANCH_ID")
    public int getBranchId() {
        return branchId;
    }

    public void setBranchId(int branchId) {
        this.branchId = branchId;
    }

    @Column(name="BRANCH_NAME")
    public String getBranchName() {
        return branchName;
    }

    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="COMPANY_ID")
    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


}
Run Code Online (Sandbox Code Playgroud)


Sne*_*kse 19

我想补充一点@JoinColumn,并不总是必须与物理信息位置相关,正如这个答案所暗示的那样.即使父表没有指向子表的表数据@JoinColumn,@OneToMany也可以组合使用.

如何在JPA中定义单向OneToMany关系

单向OneToMany,无反向ManyToOne,无连接表

它似乎只有在可用JPA 2.x+.它对于您希望子类只包含父类的ID而非完整引用的情况很有用.


Iqb*_*mid 12

我不同意ÓscarLópez在这里接受的答案。这个答案不正确!

不是@JoinColumn表示该实体是关系的所有者。而是由@ManyToOne注释执行此操作(在他的示例中)。

的关系注释,比如@ManyToOne@OneToMany@ManyToMany告诉JPA / Hibernate来创建一个映射。 默认情况下,这是通过单独的联接表完成的。


@JoinColumn

的目的@JoinColumn是创建联接列(如果尚不存在)。如果是这样,则可以使用此批注来 命名联接列。


映射

MappedBy参数的目的是指示JPA:不要创建另一个联接表,因为该关系的相对实体已经映射了该关系。



请记住: MappedBy是关系批注的属性,其目的是生成一种机制来关联两个实体,默认情况下,它们通过创建联接表来实现此功能。 MappedBy沿一个方向停止该过程。

MappedBy之所以说没有使用的实体是关系的所有者,是因为映射的机制是通过使用针对外键字段的三个映射注释之一在其类内规定的。这不仅指定了映射的性质,还指示了联接表的创建。此外,通过在外键上应用@JoinColumn批注,也可以抑制联接表,该选项将其保留在所有者实体的表内。

因此,总而言之:@JoinColumn创建一个新的连接列或重命名一个现有的连接列;同时该MappedBy参数与其他(子)类的关系注释协同工作,以便通过联接表或通过在所有者实体的关联表中创建外键列来创建映射。

为了说明其MapppedBy工作原理,请考虑以下代码。如果MappedBy要删除参数,则Hibernate实际上将创建两个联接表!为什么?因为在多对多关系中存在对称性,并且Hibernate没有选择一个方向胜过另一个方向的理由。

因此MappedBy,我们习惯告诉Hibernate,我们选择了另一个实体来决定两个实体之间关系的映射。

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    private List<Drivers> drivers;
}
Run Code Online (Sandbox Code Playgroud)

在所有者类中添加@JoinColumn(name =“ driverID”)(请参见下文)将阻止创建联接表,而是在Cars表中创建driverID外键列以构建映射:

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name = "driverID")
    private List<Drivers> drivers;
}
Run Code Online (Sandbox Code Playgroud)

  • 非常好的答案,恕我直言,比公认的答案更好。我只是想知道为什么我仍然有外键列并且没有连接表,即使我从未使用过“@JoinColumn”。 (5认同)
  • 好的,似乎不需要“@JoinColumn”来避免连接表。用注释声明两侧+用mappedBy 声明一侧也会引入这种行为。 (2认同)

小智 5

让我简单说一下。无论映射如何,您都可以在任一侧
使用@JoinColumn 。

让我们将其分为三种情况。

  1. 从分支机构到公司的单向映射。
  2. 从公司到分公司的双向映射。
  3. 仅从公司到分支机构的单向映射。

因此任何用例都属于这三个类别。那么让我解释一下如何使用@JoinColumnmappedBy

  1. 从分支机构到公司的单向映射。在 Branch 表中
    使用JoinColumn 。

  2. 从公司到分公司的双向映射。按照@Mykhaylo Adamovych 的答案所述,在公司表中
    使用mappedBy 。

  3. 从公司到分支机构的单向映射。只需在 Company 表中
    使用@JoinColumn即可。

    @Entity
    public class Company {
    
    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name="courseId")
    private List<Branch> branches;
    ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

这表示根据分支表中的外键“courseId”映射,获取所有分支的列表。注意:在这种情况下,您无法从分支机构获取公司,仅存在从公司到分支机构的单向映射。