JPA JoinColumn vs mappedBy(JPA的JoinColumn与mappedBy) 在JPA中,JoinColumn和mappedBy是用于处理实体之间关联关系的两个重要注解。 JoinColumn用于在关系的拥有方(owning side)实体中指定外键列的属性。它可以在@OneToOne、@OneToMany和@ManyToOne注解中使用。 mappedBy用于在关系的非拥有方(inverse side)实体中指定关联关系的属性。它可以在@OneToOne、@OneToMa

8 浏览
0 Comments

JPA JoinColumn vs mappedBy(JPA的JoinColumn与mappedBy) 在JPA中,JoinColumn和mappedBy是用于处理实体之间关联关系的两个重要注解。 JoinColumn用于在关系的拥有方(owning side)实体中指定外键列的属性。它可以在@OneToOne、@OneToMany和@ManyToOne注解中使用。 mappedBy用于在关系的非拥有方(inverse side)实体中指定关联关系的属性。它可以在@OneToOne、@OneToMa

这两段代码之间有什么区别?

第一段代码中,通过使用@JoinColumn注解,将Company实体与Branch实体关联起来。通过设置name属性为"companyIdRef",将Company实体中的branches属性与Branch实体中的companyId属性进行关联。同时,设置referencedColumnName属性为"companyId",指定Branch实体中的companyId属性作为关联的参考列。

第二段代码中,通过使用@mappedBy注解,将Company实体与Branch实体关联起来。通过设置mappedBy属性为"companyIdRef",指定Branch实体中的companyIdRef属性作为关联的映射列。

0
0 Comments

JPA JoinColumn vs mappedBy

JPA是Java Persistence API的缩写,它是Java EE的一部分,提供了一种方便的方法来将Java对象映射到关系数据库中的表。在JPA中,有两种方式来定义实体之间的关联关系:Join Columns和MappedBy。本文将介绍这两种方式的原因和解决方法。

单向一对多关联

如果使用@JoinColumn注解而不使用mappedBy属性,则表示有一个单向关联关系,就像下图中父实体Post和子实体PostComment之间的关系一样:

![Unidirectional one-to-many association](https://i.stack.imgur.com/ljaHg.png)

在单向一对多关联中,只有父实体定义了关联关系。在这个例子中,只有Post实体定义了对子实体PostComment@JoinColumn关联关系:

(cascade = CascadeType.ALL, orphanRemoval = true)
(name = "post_id")
private List comments = new ArrayList<>();

双向一对多关联

如果使用@JoinColumn注解并且设置了mappedBy属性,则表示有一个双向关联关系。在我们的例子中,Post实体有一个PostComment子实体的集合,而PostComment子实体有一个指向父实体Post的引用,如下图所示:

![Bidirectional one-to-many association](https://i.stack.imgur.com/i5YWM.png)

PostComment实体中,post属性的映射如下:

(fetch = FetchType.LAZY)
private Post post;

需要明确设置fetch属性为FetchType.LAZY,因为默认情况下,所有@OneToMany@ManyToOne关联关系都是急加载的,这可能会引发N+1查询问题。

Post实体中,comments关联关系的映射如下:

(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List comments = new ArrayList<>();

@JoinColumn注解的mappedBy属性指向子实体PostComment中的post属性,这样Hibernate就知道双向关联关系由Post一侧控制,负责管理基于外键列值的表关系。

对于双向关联关系,还需要两个辅助方法,比如addChildremoveChild

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}
public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

这两个方法确保了双向关联关系的两个方向保持同步。如果不同步两端,Hibernate不能保证关联状态的改变会传播到数据库。

选择哪种关联方式?

单向@JoinColumn关联性能不佳,所以应避免使用它。

最好使用效率更高的双向@JoinColumn关联性。

0
0 Comments

JPA JoinColumn vs mappedBy的问题出现的原因是在使用双向关联时,存在使用JoinColumn注解在拥有外键的一方和使用mappedBy注解在另一方的情况。问题在于这种情况下会出现物理信息重复(列名)以及无法优化的SQL查询,这会产生额外的UPDATE语句。

根据文档的说明,由于在JPA规范中,多对一关系几乎总是关系的拥有方,所以一对多关联使用注解(mappedBy=...)来进行标注。

public class Troop {
    @OneToMany(mappedBy="troop")
    public Set getSoldiers() {
    ...
}
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
} 

Troop通过属性troop与Soldier建立了双向一对多关系。在mappedBy的一方不需要定义任何物理映射。

为了映射一个双向一对多关系,使一对多关系作为拥有方,需要移除mappedBy元素,并将多对一关系的JoinColumn设置为insertable和updatable为false。这种解决方案并不是最优化的,并且会产生额外的UPDATE语句。

public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //需要重复物理信息
    public Set getSoldiers() {
    ...
}
public class Soldier {
    @ManyToOne(insertable=false, updatable=false)
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
}

我无法确定在您的第二个代码片段中如何将Troop作为拥有方,因为Soldier仍然是拥有外键引用Troop的拥有方(在MySQL中验证过)。

这段代码摘自文档页面(我加粗了)。在您的例子中,注解mappedBy="troop"是指向哪个字段?

注解mappedBy="troop"指的是Soldier类中的属性troop。在上面的代码中,由于Mykhaylo省略了属性的具体实现,所以属性看不到,但是可以通过getter getTroop()推断出属性的存在。可以查看Óscar López的回答,非常清晰,可以理解这一点。

这个例子滥用了JPA 2规范。如果作者的目标是创建双向关联,那么应该在父类上使用mappedBy,在子类上使用JoinColumn(如果需要的话)。使用这种方法,我们得到的是两个独立的单向关联:OneToMany和ManyToOne,但是仅仅是因为运气好(更多是误用),这两个关联使用了相同的外键。

如果你使用的是JPA 2.x,下面的回答可能更清晰一些。不过我建议尝试两种方法,看看Hibernate在生成表时的处理方式。如果是新项目,选择适合你需求的方法。如果是遗留数据库并且不想改变结构,选择与你的模式匹配的方法。

“物理映射”和“虚拟映射”有什么意义?

表定义会有助于澄清外键的位置。

我尝试在父表中使用JoinColumn,在我的情况下是没有外键的表,当我尝试保存时,它会先创建子类(具有外键的类),并抛出“找不到父键”的错误。

0
0 Comments

JPA JoinColumn vs mappedBy

在JPA中,我们经常会遇到需要建立实体之间关系的情况。在这种情况下,通常有两个重要的注解可以用来指定关系的拥有者和反向关系:@JoinColumn和mappedBy。

@JoinColumn注解表示该实体是关系的拥有者,即对应表中有一个指向关联表的外键列。而mappedBy属性表示在这一方是关系的反向关系,拥有者实体在“其他”实体中。这也意味着你可以从使用“mappedBy”注解的类中访问另一张表(完全双向关系)。

具体来说,对于问题中的代码,正确的注解应该如下所示:

public class Company {
    @OneToMany(mappedBy = "company",
               orphanRemoval = true,
               fetch = FetchType.LAZY,
               cascade = CascadeType.ALL)
    private List branches;
}
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyId")
    private Company company;
}

在这两种情况下,Branch都有一个指向Company的字段。

但是,Company表没有一个指向关联表的外键列,而是Branch有一个指向Company的引用。为什么你说“对应表中有一个指向关联表的外键列”?能否解释得更详细一些?

我在我的答案中使用了示例代码进行了更新。注意,在Company中使用@JoinColumn是一个错误。

那么,在OneToMany的情况下,有没有合理的情况可以使用JoinColumn呢?

不,实际上并不完全正确。如果Branch没有一个属性引用Company,但是底层表有一个列引用Company,那么可以使用@JoinColumn来映射它。这是一种不常见的情况,因为通常你会在与表对应的对象中映射列,但是它是合法的。

如果有第二个类,比如“NonProfitCompany”,其中也包含一个List branches,那么Branch如何同时映射到两个类?是否需要另一个私有变量?

是的,需要在Branch类中添加另一个类型为NonProfitCompany的私有变量。

这是另一个不喜欢ORM的原因。文档通常太模糊,对我来说,这是纠结于太多魔法领域。我一直在为这个问题苦苦挣扎,但是按照字面意思做时,子行会在其引用父行的FKey列中更新为null

在建立这种关系时,外键是必须的吗?

以这个例子为例,是否可以用外键替换Branch实体中的Company对象?private Long companyId;然后我应该指定(targetEntity = Company.class),而保持不变?

我不确定,建议你进行测试。但是肯定会破坏对象映射,你不应该对列类型进行映射,尽量使用实体对象。

我测试过了,情况很奇怪。如果我加载实体,它可以工作,但是在尝试持久化时却不能。我的策略是避免使用对象,因为我不想强制获取它,而且使用延迟获取给我带来了很多麻烦。

该注解表示该实体是关系的拥有者这句话对于在子实体中具有外键的单向关系是错误的。例如:vladmihalcea.com/…

简单而强大的回答。

0