JPA关于fetch=FetchType.EAGER级联删除的问题

探讨在使用FetchType.EAGER时遇到的实体删除难题,通过调整FetchType至LAZY实现正常删除,并保持Base实体及其关联Item实体的即时加载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天做练习的遇到了一个问题,下面重现一下错误的场景:

下面是一个Base pojo,里面包含有多个Item pojo。这里我想要在查询base的时候可以把所有的item都查出来。

所以这里使用FetchType=EAGER,会立即引起相关联实体的加载动作,就是为了能够及时抓取所有的Item。

如果使用FetchType=LAZY,就会加载不到Base的所有Iteam。

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name="Bases")
public class Base {
    @Id
    @GenericGenerator(strategy="uuid",name="uuid")
    @GeneratedValue(generator = "uuid")
    private String id;

    private String baseName;


    @OneToMany(mappedBy = "base",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    private Set<Item> items = new HashSet<Item>();


    public Base(){

    }


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Set<Item> getItems() {
        return items;
    }

    public void setItems(Set<Item> items) {
        this.items = items;
    }

    public String getBaseName() {
        return baseName;
    }

    public void setBaseName(String baseName) {
        this.baseName = baseName;
    }
}
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

@Entity
@Table(name = "Items")
public class Item {
    @Id
    @GenericGenerator(strategy="uuid",name="uuid")
    @GeneratedValue(generator = "uuid")
    private String id;

    private String itemName;


    private Integer itemNum;

    @ManyToOne
    @JoinColumn(name="b_id")
    private Base base;


    public Base getBase() {
        return base;
    }

    public void setBase(Base base) {
        this.base = base;
    }

    public Item(){

    }


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }


    public Integer getItemNum() {
        return itemNum;
    }

    public void setItemNum(Integer itemNum) {
        this.itemNum = itemNum;
    }
}

结果在测试删除item的时候,发现无法删除。但是在删除base的时候是可以级联删除掉item的,而在删除item的时候,控制台只打印出两次select语句,并没有执行删除操作。。

  @Test
    public void deleteItem(){
        ItemServiceImpl itemService = context.getBean(ItemServiceImpl.class);
        String id = "8a5e9d4464f06f6b0164f06f6d950002";
        String deleteId = itemService.deleteItem(id);
        Assert.assertTrue(deleteId.equals(id));
    }

下面是service层:

 @Transactional
    @Override
    public String deleteItem(String id) {
        return itemDao.deleteItem(id);
    }

下面是dao层:

    @Override
    public String deleteItem(String id) {
        Item item = em.getReference(Item.class, id);
        em.remove(item);
        return id;
    }

结果最后控制台只打印了两次select语句:

Hibernate: select item0_.id as id1_1_0_, item0_.b_id as b_id4_1_0_, item0_.itemName as itemName2_1_0_, item0_.itemNum as itemNum3_1_0_, base1_.id as id1_0_1_, base1_.baseName as baseName2_0_1_ from Items item0_, Bases base1_ where item0_.b_id=base1_.id(+) and item0_.id=?

Hibernate: select items0_.b_id as b_id4_1_0_, items0_.id as id1_1_0_, items0_.id as id1_1_1_, items0_.b_id as b_id4_1_1_, items0_.itemName as itemName2_1_1_, items0_.itemNum as itemNum3_1_1_ from Items items0_ where items0_.b_id=?

 之所以打印出两次select语句,是因为@ManyToOne的FetchType默认是FetchType.EAGER

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ManyToOne {
    Class targetEntity() default void.class;

    CascadeType[] cascade() default {};

    FetchType fetch() default FetchType.EAGER;

    boolean optional() default true;
}

所以在dao层中Item item = em.getReference(Item.class, id);,查询item的时候,就会加载关联的base的数据。

而@OneToMany的FetchType默认是FetchType.LAZY

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OneToMany {
    Class targetEntity() default void.class;

    CascadeType[] cascade() default {};

    FetchType fetch() default FetchType.LAZY;

    String mappedBy() default "";

    boolean orphanRemoval() default false;
}

解决方法:将@oneToMany中的fetch=FetchType.EAGER注释掉就可了。

 

但是我就是想要及时加载所有的Item数据,这种时候该怎么解决,后来尝试了很多方式,总于找到解决方法

解决方法就是把@ManyToOne的FetchType设置为FetchType.LAZY


@Entity
@Table(name = "Items")
public class Item {
    @Id
    @GenericGenerator(strategy="uuid",name="uuid")
    @GeneratedValue(generator = "uuid")
    private String id;

    private String itemName;


    private Integer itemNum;

    @ManyToOne(fetch = FetchType.LAZY)//加上LAZY
    @JoinColumn(name="b_id")
    private Base base;


    public Base getBase() {
        return base;
    }

    public void setBase(Base base) {
        this.base = base;
    }

    public Item(){

    }


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }


    public Integer getItemNum() {
        return itemNum;
    }

    public void setItemNum(Integer itemNum) {
        this.itemNum = itemNum;
    }
}

测试:

Hibernate: select item0_.id as id1_1_0_, item0_.b_id as b_id4_1_0_, item0_.itemName as itemName2_1_0_, item0_.itemNum as itemNum3_1_0_ from Items item0_ where item0_.id=?
Hibernate: delete from Items where id=?

虽然经过很多尝试找到解决方法,但是还是不太明白为什么会使用FetchType.EAGER就导致无法删除,再慢慢研究吧,研究出来以后会贴出来,也希望有人可以解答我的疑惑。。

package com.kucun.data.entity; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** * 木皮 * @author Administrator * */ @Entity @Table(name="mupi", uniqueConstraints = { @UniqueConstraint(columnNames = "name") }) @JsonSerialize(using = FullEntitySerializer.class) public class Mupi extends EntityBasis{ /** * 是否有油漆 */ @Column(name="you") @Type(type = "org.hibernate.type.BooleanType") private Boolean you; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } // 添加 OneToMany 映射 @OneToMany(mappedBy = "mupi1") // 指向 Bancai 中的 mupi1 字段 private List<Bancai> bancaisForMupi1=new ArrayList<>(); @OneToMany(mappedBy = "mupi2") // 指向 Bancai 中的 mupi2 字段 private List<Bancai> bancaisForMupi2=new ArrayList<>(); public List<Bancai> getBancaisForMupi1() { return bancaisForMupi1; } public void setBancaisForMupi1(List<Bancai> bancaisForMupi1) { this.bancaisForMupi1 = bancaisForMupi1; } public List<Bancai> getBancaisForMupi2() { return bancaisForMupi2; } public void setBancaisForMupi2(List<Bancai> bancaisForMupi2) { this.bancaisForMupi2 = bancaisForMupi2; } public Mupi() { super(); } public Boolean getYou() { return you; } public void setYou(Boolean you) { this.you = you; } } package com.kucun.data.entity; import java.lang.annotation.Annotation; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import com.kucun.data.entity.DTO.*; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** * 板材 * @author Administrator * */ @Entity @Table(name="bancai") @JsonSerialize(using = FullEntitySerializer.class) @UniqueEntity( repositoryName = "bancai", fields = {"houdu", "caizhi", "mupi1", "mupi2"}, message = "板材组合已存在" ) public class Bancai extends EntityBasis { @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = "caizhi_id") // private Caizhi caizhi; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = "mupi1_id") private Mupi mupi1; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = "mupi2_id") private Mupi mupi2; private Double houdu; @OneToOne( cascade = CascadeType.ALL, orphanRemoval = true, // 添加此配置 fetch = FetchType.LAZY ) @JoinColumn(name = "kucun_id", referencedColumnName = "id") private Kucun kucun; public Kucun getKucun() { return kucun; } public void setKucun(Kucun kucun) { this.kucun = kucun; } public Caizhi getCaizhi() { return caizhi; } public void setCaizhi(Caizhi caizhi) { this.caizhi = caizhi; } public Mupi getMupi1() { return mupi1; } public void setMupi1(Mupi mupi1) { this.mupi1 = mupi1; } public Mupi getMupi2() { return mupi2; } public void setMupi2(Mupi mupi2) { this.mupi2 = mupi2; } public Double getHoudu() { return houdu; } public void setHoudu(Double houdu) { this.houdu = houdu; } public Bancai(Integer id, Caizhi caizhi, Mupi mupi1, Mupi mupi2, Double houdu) { super( id); this.caizhi = caizhi; this.mupi1 = mupi1; this.mupi2 = mupi2; this.houdu = houdu; } public Bancai() { super(); } } 如何在mupi类中只有一个关联集合
最新发布
07-02
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值