有人可以解释 JPA 和 Hibernate 中的 mappedBy 吗?

Posted

技术标签:

【中文标题】有人可以解释 JPA 和 Hibernate 中的 mappedBy 吗?【英文标题】:Can someone explain mappedBy in JPA and Hibernate? 【发布时间】:2012-02-24 20:45:50 【问题描述】:

我是hibernate的新手,需要使用一对多和多对一的关系。这是我的对象中的双向关系,因此我可以从任一方向遍历。 mappedBy 是推荐的方法,但是,我无法理解。谁能解释一下:

推荐的使用方法是什么? 它解决了什么目的?

为了我的例子,这里是我的带有注释的类:

Airline 拥有很多 AirlineFlights 许多 AirlineFlights 属于一个 Airline

航空公司

@Entity 
@Table(name="Airline")
public class Airline 
    private Integer idAirline;
    private String name;

    private String code;

    private String aliasName;
    private Set<AirlineFlight> airlineFlights = new HashSet<AirlineFlight>(0);

    public Airline()

    public Airline(String name, String code, String aliasName, Set<AirlineFlight> flights) 
        setName(name);
        setCode(code);
        setAliasName(aliasName);
        setAirlineFlights(flights);
    

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="IDAIRLINE", nullable=false)
    public Integer getIdAirline() 
        return idAirline;
    

    private void setIdAirline(Integer idAirline) 
        this.idAirline = idAirline;
    

    @Column(name="NAME", nullable=false)
    public String getName() 
        return name;
    
    public void setName(String name) 
        this.name = DAOUtil.convertToDBString(name);
    

    @Column(name="CODE", nullable=false, length=3)
    public String getCode() 
        return code;
    
    public void setCode(String code) 
        this.code = DAOUtil.convertToDBString(code);
    

    @Column(name="ALIAS", nullable=true)
    public String getAliasName() 
        return aliasName;
    
    public void setAliasName(String aliasName) 
        if(aliasName != null)
            this.aliasName = DAOUtil.convertToDBString(aliasName);
    

    @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name="IDAIRLINE")
    public Set<AirlineFlight> getAirlineFlights() 
        return airlineFlights;
    

    public void setAirlineFlights(Set<AirlineFlight> flights) 
        this.airlineFlights = flights;
    

航空公司航班:

@Entity
@Table(name="AirlineFlight")
public class AirlineFlight 
    private Integer idAirlineFlight;
    private Airline airline;
    private String flightNumber;

    public AirlineFlight()

    public AirlineFlight(Airline airline, String flightNumber) 
        setAirline(airline);
        setFlightNumber(flightNumber);
    

    @Id
    @GeneratedValue(generator="identity")
    @GenericGenerator(name="identity", strategy="identity")
    @Column(name="IDAIRLINEFLIGHT", nullable=false)
    public Integer getIdAirlineFlight() 
        return idAirlineFlight;
    
    private void setIdAirlineFlight(Integer idAirlineFlight) 
        this.idAirlineFlight = idAirlineFlight;
    

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="IDAIRLINE", nullable=false)
    public Airline getAirline() 
        return airline;
    
    public void setAirline(Airline airline) 
        this.airline = airline;
    

    @Column(name="FLIGHTNUMBER", nullable=false)
    public String getFlightNumber() 
        return flightNumber;
    
    public void setFlightNumber(String flightNumber) 
        this.flightNumber = DAOUtil.convertToDBString(flightNumber);
    

编辑:

数据库架构:

AirlineFlights 将 idAirline 作为 ForeignKey,而 Airline 没有 idAirlineFlights。这使得 AirlineFlights 成为所有者/识别实体?

理论上,我希望航空公司成为航空公司航班的所有者。

【问题讨论】:

【参考方案1】:

MappedBy 向 hibernate 发出信号,表明关系的键在另一端。

这意味着尽管您将 2 个表链接在一起,但其中只有 1 个表对另一个表有外键约束。 MappedBy 允许您仍然从不包含约束的表链接到另一个表。

【讨论】:

你能再澄清一下吗? @Kurt Du Bois,您为什么要使用 mappedBy 而不是定义双向(每边都有 foreign_key 约束)? 因为有时候把钥匙放在两边是没有意义的。例如,假设您有一家公司和一台便携式设备。一台便携机只属于一家公司,但一家公司会有多台便携机。 很抱歉编辑人员回滚了我的答案,但实际上您的编辑没有任何附加价值。最后一句话甚至没有意义。 @KurtDuBois 如此映射,只是进入图片如何创建您的数据库,即您使用 mappedby 或在 java 端不休眠的行为类似。是吗?【参考方案2】:

通过在两个模型上指定@JoinColumn,您没有双向关系。您有两种单向关系,以及一种非常混乱的映射关系。您告诉两个模型他们“拥有” IDAIRLINE 列。真的只有其中一个真正应该! '正常'的事情是将@JoinColumn 完全从@OneToMany 端移除,而是将mappedBy 添加到@OneToMany

@OneToMany(cascade = CascadeType.ALL, mappedBy="airline")
public Set<AirlineFlight> getAirlineFlights() 
    return airlineFlights;

这告诉 Hibernate“去查看名为 'airline' 的 bean 属性,在我收集的东西上找到配置。”

【讨论】:

我对您最后关于 mappedBy 的描述感到有些困惑。事物在 db 中的组织方式是否重要? @DB:AirlineFlights 将 idAirline 作为外键。航空公司只有 idAirline 作为主键,不包含有关 AirlineFlights @ DB 的信息。 是的,这很重要。 mappedBy 中的名称告诉 Hibernate 在哪里可以找到 JoinColumn 的配置。 (在 AirlineFlight 的 getAirline() 方法上。)您将 JoinColumn 放在航空公司上的映射方式是告诉 Airline 它 负责维护另一个表中的值。可以告诉实体它“拥有”不同表中的列并负责更新它。这不是您通常想要做的事情,并且可能会导致 SQL 语句的执行顺序出现问题。 请查看编辑。在数据库级别,airlineFlight 表拥有 idAirline 作为外键列。因此,JoinColumn 应该放在相应的 ManytoOne 中的航空公司航班类/表上,因为它拥有该列? 是的,我建议这样做。这是最简单的选择,您似乎不需要任何其他东西。 “将@JoinColumn 完全从@OneToMany 一侧移除”您的意思是@ManyToOne 一侧,对吧?【参考方案3】:

mappedby 不言自明,它告诉 hibernate 不要映射这个字段。它已被此字段 [name="field"] 映射。 字段在另一个实体(name of the variable in the class not the table in the database)..

如果你不这样做,hibernate 将映射这两个关系,因为它不是 相同的关系

所以我们需要告诉 hibernate 只在一侧进行映射并在它们之间进行协调。

【讨论】:

is mappedBy 是可选的吗?因为不使用 mappedBy 我得到了相同的结果,即双向对象映射 你不能使用 on2many 和 many2one 而不在一侧使用 mappedBy 对于 many2many 你必须在一侧使用 mappedBy 感谢您指出属性值的含义,即另一个表中的字段名称。 也许hibernate并不总能自己说话,但当它说话时,至少它使用标点符号 对我来说,它本身并不能说明问题;相反,它非常混乱。只需查看有关 mappedByinversedBy 的实际问题的数量即可。其他 ORM 使用更智能的 belongsToManyhasMany 属性。【参考方案4】:

mappedby="在另一个类中创建的同一类实体的对象"

注意:-Mapped by 只能在一个类中使用,因为一张表必须包含外键约束。如果mapped by 可以在两边都应用,那么它会从两个表中删除外键,如果没有外键,两个表就没有关系。

注意:- 它可用于以下注释:- 1.@OneTone 2.@OneToMany 3.@ManyToMany

注意---它不能用于以下注释:- 1.@ManyToOne

一对一:- 在映射的任何一侧执行,但仅在一侧执行。 它将删除应用它的类的表上的外键约束的额外列。

例如。如果我们在员工对象上应用 Employee 类中的映射,则 Employee 表中的外键将被删除。

【讨论】:

【参考方案5】:

您从 ManyToOne 映射开始,然后将 OneToMany 映射也用于双向方式。 然后在 OneToMany 方面(通常是您的父表/类),您必须提及“mappedBy”(映射由子表/类完成),因此休眠不会在 DB 中创建 EXTRA 映射表(如 TableName = parent_child)。

【讨论】:

【参考方案6】:

表关系与实体关系

在关系数据库系统中,one-to-many 表关系如下所示:

请注意,该关系基于子表中的外键列(例如,post_id)。

因此,在管理 one-to-many 表关系时,只有一个事实来源。

现在,如果您采用映射到我们之前看到的 one-to-many 表关系的双向实体关系:

如果你看一下上面的图表,你可以看到有两种方法来管理这种关系。

Post 实体中,您有comments 集合:

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

并且,在PostComment 中,post 关联映射如下:

@ManyToOne(
    fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;

因为有两种方式来表示外键列,所以在将关联状态更改转换为其等效的外键列值修改时,您必须定义哪个是事实来源。

映射依据

mappedBy 属性告诉@ManyToOne 侧负责管理外键列,并且该集合仅用于获取子实体并将父实体状态更改级联到子实体(例如,删除父级也应该删除子实体)。

同步双向关联的双方

现在,即使您定义了mappedBy 属性并且子端@ManyToOne 关联管理外键列,您仍然需要同步双向关联的双方。

最好的方法是添加这两个实用方法:

public void addComment(PostComment comment) 
    comments.add(comment);
    comment.setPost(this);


public void removeComment(PostComment comment) 
    comments.remove(comment);
    comment.setPost(null);

addCommentremoveComment 方法确保双方同步。因此,如果我们添加子实体,子实体需要指向父实体,并且父实体应该将子实体包含在子集合中。

【讨论】:

你能详细说明一下实用方法吗,我有点困惑为什么我们需要同步,因为在正确映射之后,事实的来源只是多方面的,即,发表评论。是不是在addComment 中我们已经在评论对象中设置了帖子?此外,如果我们只是对 comment 执行删除作为一个单独的实体就足够了? This article 解释了为什么需要实用方法。【参考方案7】:

mappedBy 属性表征双向关联,必须在父端设置。换句话说,对于双向 @OneToMany 关联,将 mappedBy 设置为父侧的 @OneToMany 并在 mappedBy 引用的子侧添加 @ManyToOne 。通过 mappedBy,双向 @OneToMany 关联表明它反映了 @ManyToOne 子端映射。

【讨论】:

以上是关于有人可以解释 JPA 和 Hibernate 中的 mappedBy 吗?的主要内容,如果未能解决你的问题,请参考以下文章

JPA+HIBERNATE:多对多关系中的笛卡尔积

Spring Data JPA Vs Hibernate JPA Vs JPA

JPA 和 Hibernate 中的 persist() 和 merge() 有啥区别?

JPA(Hibernate)上的多个双向映射问题

Hibernate JPA中@Transient@JsonIgnoreProperties@JsonIgnore@JsonFormat@JsonSerialize等注解解释

如何从 @ManyToMany 关系中删除与 JPA 和 Hibernate 中的许多子对象的子对象