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

本文详细解析了Java持久化API(JPA)中@JoinColumn与mappedBy注解的区别,阐述了它们在实体关系映射中的作用,特别是在一对多(OneToMany)关联中的应用。文章强调了@JoinColumn用于指定实体关联的外键列,而mappedBy用于表示关系的逆方,有助于实现双向关联。
部署运行你感兴趣的模型镜像

本文翻译自:What's the difference between @JoinColumn and mappedBy when using a JPA @OneToMany association

What is the difference between: 之间有什么区别?

@Entity
public class Company {

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

and

@Entity
public class Company {

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

#1楼

参考:https://stackoom.com/question/o5gT/使用JPA-OneToMany关联时-JoinColumn和mappedBy有什么区别


#2楼

The annotation @JoinColumn indicates that this entity is the owner of the relationship (that is: the corresponding table has a column with a foreign key to the referenced table), whereas the attribute mappedBy indicates that the entity in this side is the inverse of the relationship, and the owner resides in the "other" entity. 注释@JoinColumn指示此实体是关系的所有者 (即:对应的表具有一列,该列带有被引用表的外键),而属性mappedBy指示此侧的实体是该关系的逆。关系,并且所有者位于“其他”实体中。 This also means that you can access the other table from the class which you've annotated with "mappedBy" (fully bidirectional relationship). 这也意味着您可以从用“ mappedBy”(完全双向关系)注释的类中访问另一个表。

In particular, for the code in the question the correct annotations would look like this: 特别是,对于问题中的代码,正确的注释应如下所示:

@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;
}

#3楼

@JoinColumn could be used on both sides of the relationship. 在关系的两边都可以使用@JoinColumn The question was about using @JoinColumn on the @OneToMany side (rare case). 问题是关于在@OneToMany端使用@JoinColumn (罕见情况)。 And the point here is in physical information duplication (column name) along with not optimized SQL query that will produce some additional UPDATE statements . 这里的要点是物理信息重复 (列名)以及未优化的SQL查询,这会产生一些其他的UPDATE语句

According to documentation : 根据文件

Since many to one are (almost) always the owner side of a bidirectional relationship in the JPA spec, the one to many association is annotated by @OneToMany(mappedBy=...) 由于多对一是 (几乎)总是在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() {
    ...
} 

Troop has a bidirectional one to many relationship with Soldier through the troop property. 部队通过部队属性与士兵建立了双向的一对多关系。 You don't have to (must not) define any physical mapping in the mappedBy side. 您不必(不必)在mapledBy端定义任何物理映射。

To map a bidirectional one to many, with the one-to-many side as the owning side , you have to remove the mappedBy element and set the many to one @JoinColumn as insertable and updatable to false. 以一对多方为拥有方映射双向一对多,您必须删除mapledBy元素,并将多对一设置为@JoinColumn,以将其可插入,并将其可更新为false。 This solution is not optimized and will produce some additional UPDATE statements. 此解决方案未经过优化,将产生一些其他的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() {
    ...
}

#4楼

The annotation mappedBy ideally should always be used in the Parent side (Company class) of the bi directional relationship, in this case it should be in Company class pointing to the member variable 'company' of the Child class (Branch class) 理想情况下,始终应在双向关系的父级(公司类)中使用mapledBy注释,在这种情况下,注释应在公司类中指向子类(分支类)的成员变量“ company”

The annotation @JoinColumn is used to specify a mapped column for joining an entity association, this annotation can be used in any class (Parent or Child) but it should ideally be used only in one side (either in parent class or in Child class not in both) here in this case i used it in the Child side (Branch class) of the bi directional relationship indicating the foreign key in the Branch class. 注释@JoinColumn用于指定用于连接实体关联的映射列,该注释可以在任何类(父类或子类)中使用,但理想情况下仅应在一侧使用(在父类或子类中不使用)在这两种情况下,在这种情况下,我在双向关系的子级(分支类)中使用了它,指示了分支类中的外键。

below is the working example : 以下是工作示例:

parent class , Company 家长班

@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;
    }


}

child class, Branch 子班,分支

@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;
    }


}

#5楼

I'd just like to add that @JoinColumn does not always have to be related to the physical information location as this answer suggests. 我只想补充一下, @JoinColumn不一定总是与物理信息位置相关,如答案所示。 You can combine @JoinColumn with @OneToMany even if the parent table has no table data pointing to the child table. 即使父表没有指向子表的表数据,也可以将@JoinColumn@OneToMany组合在一起。

How to define unidirectional OneToMany relationship in JPA 如何在JPA中定义单向OneToMany关系

Unidirectional OneToMany, No Inverse ManyToOne, No Join Table 单向OneToMany,Noverse ManyToOne,无联接表

It seems to only be available in JPA 2.x+ though. 不过,它似乎仅在JPA 2.x+可用。 It's useful for situations where you want the child class to just contain the ID of the parent, not a full on reference. 对于希望子类仅包含父级ID而不是完整引用的情况,这很有用。


#6楼

As I explained in this article , if you use the @OneToMany annotation with @JoinColumn , then you have a unidirectional association. 正如我在本文中所解释的,如果将@OneToMany注释与@JoinColumn一起@JoinColumn ,则您将具有单向关联。

If you use the @OneToMany with the mappedBy attribute set, you have a bidirectional association, meaning you need to have a @ManyToOne association on the child side which the mappedBy references. 如果使用@OneToManymappedBy属性集,你有一个双向关联,这意味着你需要有一个@ManyToOne对孩子一侧的关联mappedBy引用。

The unidirectional @OneToMany association does not perform very well , so you should avoid it. 单向@OneToMany关联的效果不佳 ,因此应避免使用它。

You are better off using the bidirectional @OneToMany which is more efficient . 您最好使用效率更高双向@OneToMany

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<think>我们正在解决JPA@OneToMany注解懒加载无效的问题。根据提供的引用信息,我们可以总结如下:-引用[1]说明了Hibernate对集合的分类及其注解使用,但没有直接解决懒加载无效的问题。-引用[2]提供了一种解决方案,通过在外键约束上设置`@ForeignKey(value=ConstraintMode.NO_CONSTRAINT)`来避免约束问题,但这与懒加载关系不大。-引用[3]解释了懒加载失效的可能原因,并提到使用`Hibernate.initialize()`方法可以强制初始化关联属性,但需要注意性能问题。然而,用户的问题具体是关于@OneToMany懒加载无效的常见原因及解决方案。根据经验,@OneToMany默认就是懒加载,但有可能会出现懒加载无效的情况,比如:1.在非事务环境下访问懒加载属性,导致无法从数据库加载数据(出现LazyInitializationException)。2.序列化或反序列化过程中触发了懒加载属性的加载(例如在Controller层返回给前端,JSON序列化会触发懒加载,但此事务已关闭)。3.在视图层(如JSP)中访问懒加载属性,同样因为事务已关闭而导致异常。4.可能错误配置了fetch类型为EAGER(尽管@OneToMany默认是LAZY,但有可能被覆盖)。针对用户的问题,我们重点参考引用[3]以及常见的解决方案。以下是解决方案:1.确保在事务边界内访问懒加载属性:确保在事务未提交前(例如在Service层的方法中,方法上有@Transactional注解)访问懒加载属性。这样Hibernate才能从数据库加载数据。2.避免在序列化触发懒加载:使用DTO(DataTransferObject)代替实体直接返回给前端,或者配置JSON序列化工具(如Jackson)忽略未加载的属性,或者使用`@JsonIgnoreProperties`注解忽略关联属性。3.使用OpenSessionInView模式(OSIV):该模式会延长Session生命周期到视图渲染结束,但要注意可能带来的性能问题数据库连接占用。在SpringBoot中可以通过配置`spring.jpa.open-in-view`来开启(默认开启),但通常不推荐开启,因为可能导致连接泄漏或性能问题。4.显式初始化关联属性:如引用[3]所述,在事务方法内,使用`Hibernate.initialize()`来显式初始化关联属性,例如:```java@TransactionalpublicSomeEntitygetEntityWithInitializedCollection(Longid){SomeEntityentity=entityManager.find(SomeEntity.class,id);//显式初始化关联集合Hibernate.initialize(entity.getSomeCollection());returnentity;}```但注意,这样会立即加载集合,增加了数据库查询。5.使用FetchJoin:在查询使用JOINFETCH来加载关联集合,这样在同一个查询中加载数据,避免懒加载问题。例如:```java@Query("SELECTeFROMSomeEntityeJOINFETCHe.someCollectionWHEREe.id=:id")SomeEntityfindEntityWithCollection(@Param("id")Longid);```6.检查是否配置了Fetch类型:确认在@OneToMany上没有设置`fetch=FetchType.EAGER`,或者是否被其他配置覆盖(如EntityGraph)。7.检查是否在实体类中使用了`toString()`方法,因为`toString()`可能会打印关联属性,从而触发懒加载(在非事务环境中会出错)。应避免在`toString()`中包含懒加载属性。8.考虑使用`FetchType.LAZY`的同,在需要的地方使用`@LazyToOne``@LazyCollection`等注解进行更细粒度的控制(但需要Hibernate扩展支持)。具体到引用中提供的解决方案:-引用[3]提到的`Hibernate.initialize()`是一种解决方案,但要注意使用场景。总结建议:首先确认问题是否由事务边界导致(例如是否在事务外访问了懒加载属性)。如果是,则采用事务内访问、DTO传输或OSIV(谨慎使用)等方法。如果确实需要加载集合,可以在查询使用FetchJoin一次加载。相关问题:1.如何理解JPAHibernate中的懒加载(LazyLoading)?2.什么是OpenSessionInView模式?它有什么优缺点?3.如何在JPA中正确使用JOINFETCH来避免N+1查询问题?</think> ### JPA @OneToMany 懒加载无效的解决方案 在JPA中,`@OneToMany`默认采用懒加载(Lazy Loading),但实际开发中常遇到懒加载失效问题。以下是基于引用内容的完整解决方案: #### 常见原因及解决方案 1. **外键约束干扰** - 问题:`@ForeignKey`注解可能强制建立数据库约束,破坏懒加载机制。 - 修复方案:禁用物理外键约束(引用[2]): ```java @OneToMany @JoinColumn(name = "fk_column") @org.hibernate.annotations.ForeignKey(name = "none") // 已废弃但有效 // 或使用JPA标准方式: @ForeignKey(value = ConstraintMode.NO_CONSTRAINT) ``` 2. **序列化/反序列化触发加载** - 问题:JSON序列化工具(如Jackson)自动访问属性导致懒加载被触发(引用[3])。 - 解决方案: ```java @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) @Entity public class Parent { @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) private List<Child> children; } ``` 3. **代理对象未初始化** - 问题:Hibernate返回代理对象但不触发SQL查询。 - 强制初始化(引用[3]): ```java @Transactional public Parent getParent(Long id) { Parent parent = entityManager.find(Parent.class, id); Hibernate.initialize(parent.getChildren()); // 显式触发加载 return parent; } ``` 4. **FetchType配置错误** - 问题:被关联方配置覆盖: ```java @Entity public class Parent { @OneToMany(fetch = FetchType.LAZY) // ✅ 正确配置 private List<Child> children; } @Entity public class Child { @ManyToOne(fetch = FetchType.EAGER) // ❌ 此配置会覆盖父类懒加载 private Parent parent; } ``` 5. **Open Session in View模式问题** - 解决方案:在Spring Boot中配置: ```yaml spring: jpa: open-in-view: false # 避免Session生命周期过长 ``` #### 最佳实践推荐 1. **实体类设计规范**(引用[1]) ```java // 明确指定集合语义 @OneToMany @org.hibernate.annotations.LazyCollection(LazyCollectionOption.EXTRA) private Set<Child> children; // Set语义确保懒加载稳定性 ``` 2. **事务边界控制** ```java @Service public class ParentService { @Transactional(readOnly = true) public Parent getParentWithChildren(Long id) { return parentRepository.findById(id) .orElseThrow(...); } } ``` 3. **性能监控** - 启用Hibernate统计: ```properties spring.jpa.properties.hibernate.generate_statistics=true ``` > 引用说明:外键约束配置可避免懒加载失效[^2],代理对象需显式初始化[^3],集合类型选择影响加载行为[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值