You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot JPA中ManyToMany映射下deleteById()方法失效问题

问题分析:删除InsurancePolicy时无法联动删除PolicyCounty

实体代码

InsurancePolicy.java

@Entity
@Table(name = "insurance_policy")
public class InsurancePolicy {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="policy_id")
    private long policyId;

    @Column(name="policy_name",nullable = false,unique = true)
    private String policyName;

    //Short description
    @Column(name="policy_description",nullable = false)
    private String policyDescription;

    //Long description
    @Column(name="choose_description",nullable = false)
    private String chooseDescription;


    //many policies can belong to one category
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id", nullable = false)
    private Company company;

    //many policies can belong to one category
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="category_id",nullable=false)
    private Category category;

    @ManyToMany(mappedBy = "queriedPolices",fetch = FetchType.LAZY,cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    private Set<User> usersQueried=new HashSet<>();

    @OneToMany(mappedBy = "policy", cascade = {CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REMOVE},fetch = FetchType.LAZY)
    private Set<PolicyCounty> policyCounties = new HashSet<>();
}

PolicyCounty.java(关联表)

@Data
@Entity
@Table(name="policy_county")
public class PolicyCounty {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="policy_county_id")
    private long policyStateId;

    @ManyToOne
    @JoinColumn(name="policy_id")
    private InsurancePolicy policy;

    @ManyToOne
    @JoinColumn(name = "county_id")
    private County county;

    @Column(name="cost",nullable = false)
    private double cost;
}

County.java

@Entity
@Data
@Table(name="county")
public class County {
    @Id
    @Column(name="countyid")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long countyId;

    @Column(name="countyname",nullable = false,unique = true)
    private String countyName;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "adminId", nullable = false)
    private Admin admin;

    @OneToMany(mappedBy = "county", cascade = {CascadeType.MERGE,CascadeType.PERSIST},fetch = FetchType.LAZY)
    private Set<PolicyCounty> policyStates = new HashSet<>();
}

问题描述

希望删除InsurancePolicy实体时,自动删除PolicyCounty表中对应的关联记录,编写了如下删除方法:

@Override
public long deletePolicy(long policyId) {
    boolean isPresent=insurancePolicyRepository.existsById(policyId);
    if(isPresent){
        insurancePolicyRepository.deleteById(policyId);
        return insurancePolicyRepository.count();
    }
    throw new ResourceNotFoundException("Policy with id "+policyId+" not found");
}

执行删除操作时,InsurancePolicy和PolicyCounty中的记录都未被删除,且无任何异常抛出,怀疑是级联配置问题。

原因分析与解决方案

核心问题

  1. 级联操作方向错误:InsurancePolicy中的@OneToMany标注了mappedBy = "policy",说明关联关系由PolicyCounty的policy字段维护(即PolicyCounty是关联拥有方)。JPA的级联操作仅对拥有方生效,当前CascadeType.REMOVE配置在非拥有方的InsurancePolicy上,删除政策时不会触发对PolicyCounty的级联删除。
  2. 外键约束阻止删除:PolicyCounty表的policy_id是指向insurance_policy的外键,默认外键约束会阻止删除被引用的政策记录,而代码未捕获该约束异常,导致删除操作静默失败。

解决方案

方案1:手动删除关联记录后再删政策

在删除InsurancePolicy前,先清除对应的PolicyCounty记录,确保外键约束不被触发:

@Override
@Transactional // 需确保事务覆盖整个操作流程
public long deletePolicy(long policyId) {
    InsurancePolicy policy = insurancePolicyRepository.findById(policyId)
            .orElseThrow(() -> new ResourceNotFoundException("Policy with id "+policyId+" not found"));
    
    // 先删除关联的PolicyCounty
    policyCountyRepository.deleteByPolicy(policy);
    
    // 再删除InsurancePolicy
    insurancePolicyRepository.delete(policy);
    
    return insurancePolicyRepository.count();
}

同时在PolicyCountyRepository中添加对应方法:

public interface PolicyCountyRepository extends JpaRepository<PolicyCounty, Long> {
    void deleteByPolicy(InsurancePolicy policy);
}

方案2:数据库层面配置外键级联删除

修改policy_county表的policy_id外键,配置ON DELETE CASCADE,删除政策时数据库自动删除关联的PolicyCounty记录。若使用JPA自动建表,可通过@JoinColumn配置:

// 在PolicyCounty的policy字段上修改
@ManyToOne
@JoinColumn(
    name="policy_id",
    foreignKey = @ForeignKey(
        name = "fk_policy_county_policy",
        foreignKeyDefinition = "FOREIGN KEY (policy_id) REFERENCES insurance_policy(policy_id) ON DELETE CASCADE"
    )
)
private InsurancePolicy policy;

方案3:调整级联配置到关联拥有方(谨慎使用)

修改PolicyCounty中的@ManyToOne关联,添加级联删除,但此方式会导致删除PolicyCounty时同步删除InsurancePolicy,需结合业务场景判断:

@ManyToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name="policy_id")
private InsurancePolicy policy;

内容的提问来源于stack exchange,提问作者ayush

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 22:54:54