记一次JPA级联问题&CascadeType详解

66 篇文章 12 订阅
在使用Spring Boot JPA进行多对多关系映射时,遇到`detachedentitypassedtopersist`错误。问题源于在级联操作中设置了`CascadeType.ALL`,导致尝试持久化已存在的数据。解决方法是根据业务需求选择合适的级联类型,避免不必要的数据重复保存。示例代码展示了User和Role实体的多对多关系,以及级联操作的详细解释。
摘要由CSDN通过智能技术生成

遇到的问题

首先我在用springboot-jpa写一个多对多demo,进行插入数据的时候遇到了如下的问题:
detached entity passed to persist
请添加图片描述
大概的意思是该数据插入的时候,使用了级联表中已经有的数据,该条数据的id已经存在,无法继续插入,因此:detached entity passed to persist。
这个是什么问题产生的呢?
这个问题搞了很久,网上的说法也是千奇百怪,后来突然恍然一悟,为什么会要插入数据插不进去,可能会发生的操作是什么,突然就想明白,是做了多对多操作,jpa的多对多操作的特点就是需要做级联,而级联的时候就可能系统认为是插入数据,所有的数据都需要进行持久化,就算数据库里面已经有的数据也进行了再次持久化。后来找到了@ManyToMany,果然注解属性的级联权限设置了:cascade = CascadeType.ALL,其中CascadeType.ALL的级联权限中包括了CascadeType.PERSIST,这个就是罪魁祸首。

JPA多对多级联的demo

级联代码如下:
User.java

package cn.kt.securitytest2.domin;

/**
 * Created by tao.
 * Date: 2021/10/27 10:39
 * 描述:
 */

import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import java.io.Serializable;
import java.util.*;

@Entity
@Getter
@Setter
@Table(name = "user")
public class User implements UserDetails, Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    private String password;

    @Column(name = "enabled")
    private Boolean enabled;

    @Column(name = "locked")
    private Boolean locked;

    @Column(name = "expired")
    private Boolean expired;

    @Column(name = "credentialsexpire")
    private Boolean credentialsExpire;

    @ManyToMany(targetEntity = Role.class, fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
    @JoinTable(name = "user_role",
            //joinColumns配置当前对象在中间表中的外键(第一个参数是中间表的字段,第二个参数是本表对应的字段)
            joinColumns = {@JoinColumn(name = "uid", referencedColumnName = "id")},
            //inverseJoinColumns配置对方对象在中间表中的外键
            inverseJoinColumns = {@JoinColumn(name = "rid", referencedColumnName = "id")}
    )
    private Set<Role> roles = new HashSet<Role>();

}

Role.java

package cn.kt.securitytest2.domin;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;

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

/**
 * Created by tao.
 * Date: 2021/10/27 10:39
 * 描述:
 */
@Entity
@Getter
@Setter
@Table(name = "role")
public class Role implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "nameen")
    private String nameEN;

    @Column(name = "namezh")
    private String nameZh;

    //多对多关系映射
    @ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER)
    @JsonIgnore
    private Set<User> users = new HashSet<User>();

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", nameEN='" + nameEN + '\'' +
                ", nameZh='" + nameZh + '\'' +
                '}';
    }
}

其中user和role对应的是两张数据库表,还有一张关联的中间表user_role.

JPA级联操作的详解

通过以上的代码可以看到,User和Role的级联权限是CascadeType.ALL。
但经过实践得出:不要随便给all权限操作。应该根据业务需求选择所需的级联关系。否则可能酿成大祸。

级联的属性:

  1. CascadeType.PERSIST
    级联持久化(保存)操作:持久保存拥有方实体时,也会持久保存该实体的所有相关数据。这个属性就是造成上面问题的关键。当你保存一天条数据时,所有的关联数据都会进行保存,无论数据库里面有没有,但有时候我们是需要这样的级联操作的。
  2. CascadeType.REMOVE
    级联删除操作:删除当前实体时,与它有映射关系的实体也会跟着被删除。
  3. CascadeType.DETACH
    级联脱管/游离操作:如果你要删除一个实体,但是它有外键无法删除,你就需要这个级联权限了。它会撤销所有相关的外键关联。
  4. CascadeType.REFRESH
    级联刷新操作:假设场景 有一个订单,订单里面关联了许多商品,这个订单可以被很多人操作,那么这个时候A对此订单和关联的商品进行了修改,与此同时,B也进行了相同的操作,但是B先一步比A保存了数据,那么当A保存数据的时候,就需要先刷新订单信息及关联的商品信息后,再将订单及商品保存。
  5. CascadeType.MERGE
    级联更新(合并)操作:当Student中的数据改变,会相应地更新Course中的数据。
  6. CascadeType.ALL
    清晰明确,拥有以上所有级联操作权限。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不愿意做鱼的小鲸鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值