Jpa-多表关联-OneToMany与ManyToOne

2 篇文章 0 订阅


OneToManyhibernate 中用于维护一对多的对应关系

ManyToOnehibernate 中用于维护多对一的对应关系

准备

package com.mfyuan.model;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
@Entity
@Table(name = "t_process_batch", schema = "test")
public class TProcessBatch {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @Column(name = "name", length = 500)
    private String name;

    @JoinColumn(name = "batch_id")
    @OneToMany(cascade = CascadeType.PERSIST)
    private List<TProcess> processList;
}

-- auto-generated definition
create table t_process_batch
(
    id   bigint auto_increment
        primary key,
    name varchar(500) null comment '流程批次名称'
);

-- auto-generated definition
create table t_process
(
    id       bigint auto_increment
        primary key,
    batch_id bigint       null comment '流程批次id',
    name     varchar(500) null comment '流程名称'
);

JoinColumn

OneToMany 一起使用的情况下。

JoinColumn 用于定义一对多的外键关系。

name:用 目标表 的那个字段作为 当前表 的外键,默认是 实体名_字段名
referencedColumnName:与 当前表 用那个字段进行关联,默认为 当前表的主键

注意:这里与 OneToOne 是反过来的

OneToMany

targetEntity

关联的目标实体类。默认为存储关联的字段的类型。

cascade*

表明那些操作需要级联操作。默认为空。但是查询的时候是会进行级联查询的。

PERSIST

只有插入(INSERT)操作时进行级联操作 插入主表的同时会插入子表。

注意: 这里其实很奇怪,为什么插入子表后再通过 update 来对 batch_id 进行更新。是这样 t_process 中并没有维护与 t_process_batch 的关系,所以采用先插入后更新的方式处理。后面使用 mappedBy@ManyToOne 即可解决先插入后更新的问题了。

image-20240806162450930

注意: 插入操作时,如果没有标注 PERSIST 方法时则会出现更奇怪的问题,只插入主表,没有插入子表,但是对子表进行了更新操作。当然这里的 update 操作肯定是没有用的。

image-20240806163019294

// 修改TProcessBatch.java中的 @OneToMany(cascade = {CascadeType.PERSIST})

@Test
public void testInsert(){
    TProcessBatch tProcessBatch = new TProcessBatch();
    tProcessBatch.setName("INSERT_PROCESS");
    List<TProcess> processList = new ArrayList<>();
    processList.add(new TProcess("INSERT_1"));
    processList.add(new TProcess("INSERT_2"));
    processList.add(new TProcess("INSERT_3"));
    tProcessBatch.setProcessList(processList);
    repository.save(tProcessBatch);
}
MERGE

只有更新(UPDATE)操作时进行级联操作,更新时如果将关联属性修改为 null 会把当前表的关联的字段改为 null。默认是不会删除关联表的行数据( 注意这里不会将关联表的行数据给删除,需要删除的话则需要开启 orphanRemoval)。

// 修改TProcessBatch.java中的 @OneToMany(cascade = {CascadeType.MERGE})

@Test
public void testUpdate(){
    TProcessBatch tProcessBatch = new TProcessBatch();
    tProcessBatch.setId(18L);
    tProcessBatch.setName("UPDATE_PROCESS");

    List<TProcess> processList = new ArrayList<>();
    processList.add(new TProcess("UPDATE_1"));
    processList.add(new TProcess("UPDATE_2"));
    tProcessBatch.setProcessList(processList);
    repository.save(tProcessBatch);
}

更新有两种情况

  • 已有子表的关联信息

    先查询是否存在主表及子表信息,更新完主表后,对子表进行插入,插入完成后,将所有旧的关联数据的关联字段修改为 null再对将新插入的子表的关联字段进行更新

    image-20240806173124604

  • 没有子表的关联信息

    先查询是否存在主表及子表信息,更新完主表后,对子表进行插入,再对子表的关联字段进行更新

    image-20240806173028571

    image-20240806173519245

REMOVE

只有删除(DELETE)操作时进行级联操作,删除主表的同时会把子表也删除

// 修改TProcessBatch.java中的 @OneToMany(cascade = {CascadeType.REMOVE})

@Test
public void testRemove(){
    repository.deleteById(22L);
}

查询主表与子表的信息,将子表中所有存在关系行的关联字段改为 null,并删除子表信息,再删除主表信息。

image-20240806174338441

没有添加 CascadeType.REMOVE 时不会对子表进行删除。

image-20240806174051180

REFRESH

EntityManager.refresh() 方法用于重新加载实体的状态,从数据库中获取最新的数据,并覆盖当前持久化上下文中的实体状态

orphanRemoval

当进行更新操作时,将清空主表的关联数据或修改主表的关联数据时,会删除关联的子表数据。默认为 false(只是将子表的关联字段改为 null)。

// 修改TProcessBatch.java中的 @OneToMany(cascade = {CascadeType.MERGE},orphanRemoval=true)

@Test
public void testOrphanRemoval(){
    TProcessBatch tProcessBatch = new TProcessBatch();
    tProcessBatch.setId(26L);
    tProcessBatch.setName("UPDATE_PROCESS1");
    List<TProcess> processList = new ArrayList<>();
    tProcessBatch.setProcessList(processList);
    repository.save(tProcessBatch);
}

image-20240806221825106

当对原有的子表集合进行操作时,会先将原有子表中所有与当前表有关联数据行的关联字段修改为 null,并对删除这些行。默认(false):只会改为 null,不会删除。

fetch

  • LAZY:(默认与 @OneToOne 相反)如果是 LAZY 则是当我们使用关联表对象的时候才会去进行查询。注意 使用 LAZY 时需要 @Transactional 中进行使用,因为如果查询完 session 关闭了就不能从里面获取数据了。
  • EAGER:立即获取数据与主表一起查出
    • 为什么不默认呢?因为如果这样的一对多关系存在很多个的话,一起查出来性能能很差,查出来的数据也不一定用。
// 默认情况下不加@Transactional 在使用子表数据的时候会报错
@Test
@Transactional
public void testOneToOneFetchLazy(){
    TUserAccount tUserAccount = userAccountRepository.findById(6L).get();
    System.out.println("================wait================");
    System.out.println(tUserAccount.getUser());
}

img

// 修改TProcessBatch.java中的 @OneToMany(cascade = {CascadeType.ALL},orphanRemoval=true,fetch = FetchType.EAGER)

image-20240806223005179

先只查主表,如果我们 没有使用则不会对关联表进行查询,而到我们获取关联表信息的时候再去对关联表进行查询。

optional

是否允许为空,默认是 true。为 false 时不能将关联字段设置为 null。表示为非空的关联关系。

MappedBy*

主要作用用于, 进行 双向关联。表示关联关系为 当前字段类型的的实体 来进行维护,指定的值为 目标类型 中的外键字段。这里删除,更新,不会对关联进行操作。

一对多 <==> 多对一

当使用 MappedBy 需要在 的这个关系中维护一个 @ManyToOne 的关联关系。

只需要通过 @OneToMany 通过 MappedBy 使用这个关系即可。

@Entity
@Table(name = "t_process_batch", schema = "test")
public class TProcessBatch {
    //...
	
    // 不在使用@JoinColumn来维护关联关系。
    @OneToMany(mappedBy = "batch", cascade = {CascadeType.ALL},orphanRemoval = true,fetch = FetchType.EAGER)
    private List<TProcess> processList;
}

@Entity
@Table(name = "t_process", schema = "test")
public class TProcess {

    // 但是这里要使用JoinColumn来维护多对一的关系 name 只当前表用来关联的字段,referencedColumnName:默认为关联表的主键
    @JoinColumn(name = "batch_id")
    @ManyToOne
    private TProcessBatch batch;
}

@Test
public void testQueryManyToOne(){
    TProcess tProcess = processRepository.findById(73L).get();
    System.out.println(tProcess);
}

image-20240806225852117

因为进行了双向绑定,先通过查询将 TProcessTProcessBatch 多对一的数据查询出来,再通过得到的 batch_id 去进行 一对多 的查询。

ManyToOne

通常情况下:在进行插入的时候会查 ManyToOne 这个对象中进行,而查询则会在 OneToMany的对象中进行。这样更符合业务逻辑。

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

假女吖☌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值