0Pricing
Java Academy · 课时

一对多与多对多关系

使用级联类型和获取策略映射 @OneToMany、@ManyToMany 与 @JoinTable

一对多与多对多关系 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

JPA 中的关系类型

JPA 支持四种关系类型:@OneToOne、@OneToMany、@ManyToOne、@ManyToMany。每种类型都会映射到外键约束或连接表约束。

@ManyToOne:拥有方

多对一关联(例如,多条订单属于一个用户)通过 @ManyToOne 映射,并使用 @JoinColumn 指定外键。

@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

@OneToMany:反向方

反向方(用户拥有多条订单)使用 @OneToMany(mappedBy = "user")。mappedBy 属性指向拥有方中保存 FK 的字段。

@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Order> orders = new ArrayList<>();
}

双向一致性辅助方法

对于双向关联,请在辅助方法中维护关联的两端,以便在刷新到数据库前保持对象图一致。

public void addOrder(Order order) {
    orders.add(order);
    order.setUser(this); // keep both sides in sync
}
public void removeOrder(Order order) {
    orders.remove(order);
    order.setUser(null);
}

级联类型

CascadeType.ALL 会将所有操作(持久化、合并、删除、刷新、分离)从父实体传播到子实体。PERSIST 和 MERGE 是常见且安全的选择。删除父实体时,REMOVE 会同时删除子实体。

@OneToMany(mappedBy = "user",
           cascade = {CascadeType.PERSIST, CascadeType.MERGE},
           orphanRemoval = true)
private List<Order> orders;

orphanRemoval:自动删除子实体

orphanRemoval = true 会在子实体从父实体的集合中移除时将其删除,即使没有显式调用 entityManager.remove()。

user.getOrders().remove(order); // triggers DELETE for the orphaned order on flush

@ManyToMany 与 @JoinTable

多对多关联(例如学生与课程)使用连接表。在拥有方通过 @JoinTable 定义连接表;反向方使用 mappedBy。

@Entity
public class Student {
    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id"))
    private Set<Course> courses = new HashSet<>();
}

连接表中的额外列

当连接表需要额外列(例如注册日期)时,请将 @ManyToMany 转换为两个 @OneToMany / @ManyToOne 关联,并创建一个表示连接表的新实体。

@Entity
public class Enrollment {
    @ManyToOne Student student;
    @ManyToOne Course course;
    private LocalDate enrolledAt;
}

FetchType:LAZY 与 EAGER

默认加载策略:@ManyToOne 和 @OneToOne 为 EAGER(立即加载);@OneToMany 和 @ManyToMany 为 LAZY(访问时加载)。对于集合,请始终使用 LAZY,以避免 N+1 查询。

// Always override to LAZY:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;

N+1 查询问题

在循环中访问延迟加载的集合时,每个实体都会触发一次查询(N+1)。请使用 JPQL 中的 JOIN FETCH 或 @EntityGraph,在一次查询中加载该集合。

// N+1 problem:
List<User> users = repo.findAll();
users.forEach(u -> u.getOrders().size()); // 1 + N queries!
// Fix:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();

集合使用 Set 还是 List

对 Set 使用 @ManyToMany(可避免重复的连接表行)。当顺序很重要时,对 @OneToMany 使用 List。请勿将 List 与 @ManyToMany 一起使用,否则 Hibernate 会删除并重新插入所有行。

快速检查

@OneToMany 注解中的 mappedBy 表示什么?

回顾

在拥有方(FK 方)使用 @ManyToOne + @JoinColumn。在反向方使用 @OneToMany(mappedBy=...)。集合始终使用 LAZY。使用 JOIN FETCH 解决 N+1 问题。多对多关联需要额外列时,请使用连接实体。

常见问题解答

「一对多与多对多关系」课时是免费的吗?

是的 — 「一对多与多对多关系」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「一对多与多对多关系」这节课中我会学到什么?

使用级联类型和获取策略映射 @OneToMany、@ManyToMany 与 @JoinTable 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「一对多与多对多关系」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 JPA 注解映射实体
  2. Spring Data 仓库与查询方法
  3. 一对多与多对多关系
  4. 分页、排序与投影
← 返回 Java Academy