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

ORA-02291完整性约束违反求助:插入JV_SNAPSHOT时父键未找到

JPA一对一映射插入数据触发外键约束错误问题

我尝试向JV_SNAPSHOT、JV_GLOBAL_ID、JV_COMMIT三个表插入数据,对应的实体类如下:

@Entity
@Table(name = "JV_SNAPSHOT")
public class JVSnapshot {
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jv_snapshot_pk_seq")
    @SequenceGenerator(name="jv_snapshot_pk_seq", sequenceName = "JV_SNAPSHOT_PK_SEQ")
    @Column(name = "SNAPSHOT_PK")
    private long snapshotID;
    
    @Column(name="VERSION")
    private long version;
    
    @Column(name="GLOBAL_ID_FK")
    private long globalID;
    
    @Column(name="COMMIT_FK")
    private long commitID;

    @Column(name = "TYPE")
    private String type;
    
    @Column(name = "MANAGED_TYPE")
    private String managedType;
    
    @Column(name = "STATE")
    private String state;
    
    @Column(name = "CHANGED_PROPERTIES")
    private String changedProperties;
    
    @OneToOne(mappedBy = "jvSnapshot")
    //@JoinColumn(name = "id")
    private JVGlobalID jvGlobalID;
    
    @OneToOne(mappedBy = "jvSnapshot")
    //@JoinColumn(name = "id")
    private JVCommit jvCommit;
}
@Entity
@Table(name = "JV_GLOBAL_ID")
public class JVGlobalID {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jv_global_id_pk_seq")
    @SequenceGenerator(name="jv_global_id_pk_seq", sequenceName = "JV_GLOBAL_ID_PK_SEQ")
    @Column(name = "GLOBAL_ID_PK")
    private long globalID;

    @Column(name = "LOCAL_ID")
    private String localID;
    
    @Column(name = "FRAGMENT")
    private String fragment;
    
    @Column(name = "TYPE_NAME")
    private String typeName;
    
    @Column(name="OWNER_ID_FK")
    private long ownerID;
    
    @OneToOne @MapsId
    private JVSnapshot jvSnapshot;
}
@Entity
@Table(name = "JV_COMMIT")
public class JVCommit {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jv_commit_pk_seq")
    @SequenceGenerator(name="jv_commit_pk_seq", sequenceName = "JV_COMMIT_PK_SEQ")
    @Column(name= "COMMIT_PK" , insertable = true, updatable = false)
    private long id;

    @Column(name = "AUTHOR")
    private String author;
    
    @Column(name = "COMMIT_DATE")
    private Date commitDate;
    
    @Column(name = "COMMIT_DATE_INSTANT")
    @JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    private String commitDateInstant;
    
    @Column(name="COMMIT_ID")
    private long commitId;  
    
    @OneToOne @MapsId
    private JVSnapshot jvSnapshot;
}

包含计算值及插入数据完整业务逻辑的Service类如下:

JVSnapshot jvSnapshot = new JVSnapshot();
//inserting data into JV_GLOBAL_ID table
System.out.println("Inserting in JV_GLOBAL_ID table");
JVGlobalID jvGlobalId = new JVGlobalID();
jvGlobalId.setTypeName("com.dev.neo.models.Customer");
jvGlobalId.setLocalID("99999");
jvCommitGlobalIdRepository.save(jvGlobalId); //globalID generated (PK)
System.out.println("Data inserted in jvcommitGlobalrespository");
                    
//inserting data into JV_COMMIT table
System.out.println("Inserting in JV_COMMIT table");
JVCommit jvCommit = new JVCommit();
List<Long> jvCommitObjectList = jvCommitRepository.getMaxCommitId();
Long commitId = jvCommitObjectList.get(0) + 1;
String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
String date = simpleDateFormat.format(new Date());
jvCommit.setAuthor(req.getCheckerId());
jvCommit.setCommitDate(new Date());
jvCommit.setCommitDateInstant(date);
jvCommit.setCommitId(commitId);
System.out.println("Data inserted Successfully");
jvSnapshot.setJvCommit(jvCommit);
jvCommitRepository.save(jvCommit);   //id generated (PK)
System.out.println("JV_COMMIT Data saved Successfully");
jvSnapshot.setCommitID(jvCommit.getId());
jvSnapshotRepository.save(jvSnapshot);
                    
//inserting data into JV_SNAPSHOT table
System.out.println("Inserting in JV_SNAPSHOT table");
//CLOB Datatypes variables
                    
String existingCustomerIDNew = "somenewString";
String existingCustomerID = "someString";
jvSnapshot.setState(existingCustomerIDNew);
jvSnapshot.setChangedProperties(existingCustomerID);
jvSnapshot.setManagedType("com.dev.neo.models.Customer");
jvSnapshot.setType("UPDATE");
jvSnapshot.setVersion(2);
jvSnapshot.setGlobalID(jvGlobalId.getGlobalID());
System.out.println("Data inserted in all JV");

执行所有步骤后,数据未写入表中,且出现错误:
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (SYSTEM.JV_SNAPSHOT_GLOBAL_ID_FK) violated - parent key not found

JV_SNAPSHOT_GLOBAL_ID_FK是JV_SNAPSHOT表中GLOBAL_ID_FK列的外键约束名,该列关联JV_GLOBAL_ID表的主键列GLOBAL_ID_PK。我已尝试多种方式声明这些表的一对一映射,但均未解决问题。


问题根源

  1. 映射配置混乱:@MapsId配置要求JVGlobalID、JVCommit的主键与JVSnapshot主键一致,但你又给这两个实体配置了独立序列生成主键,同时JVSnapshot手动维护外键字段,导致映射逻辑冲突。
  2. 插入顺序与关联维护错误:先保存关联实体但未建立双向关联,后续给JVSnapshot设置外键后未重新保存,且插入JV_SNAPSHOT字段的时机晚于首次保存操作。

修复方案

1. 修正实体类映射

以JVSnapshot作为关联主控方,由JPA自动维护外键,删除手动维护的外键字段:

JVSnapshot.java

@Entity
@Table(name = "JV_SNAPSHOT")
public class JVSnapshot {
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jv_snapshot_pk_seq")
    @SequenceGenerator(name="jv_snapshot_pk_seq", sequenceName = "JV_SNAPSHOT_PK_SEQ")
    @Column(name = "SNAPSHOT_PK")
    private long snapshotID;
    
    @Column(name="VERSION")
    private long version;

    @Column(name = "TYPE")
    private String type;
    
    @Column(name = "MANAGED_TYPE")
    private String managedType;
    
    @Column(name = "STATE")
    private String state;
    
    @Column(name = "CHANGED_PROPERTIES")
    private String changedProperties;
    
    // 主控方,维护GLOBAL_ID_FK外键
    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "GLOBAL_ID_FK", referencedColumnName = "GLOBAL_ID_PK")
    private JVGlobalID jvGlobalID;
    
    // 主控方,维护COMMIT_FK外键
    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "COMMIT_FK", referencedColumnName = "COMMIT_PK")
    private JVCommit jvCommit;

    // 省略getter/setter
}

JVGlobalID.java

@Entity
@Table(name = "JV_GLOBAL_ID")
public class JVGlobalID {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jv_global_id_pk_seq")
    @SequenceGenerator(name="jv_global_id_pk_seq", sequenceName = "JV_GLOBAL_ID_PK_SEQ")
    @Column(name = "GLOBAL_ID_PK")
    private long globalID;

    @Column(name = "LOCAL_ID")
    private String localID;
    
    @Column(name = "FRAGMENT")
    private String fragment;
    
    @Column(name = "TYPE_NAME")
    private String typeName;
    
    @Column(name="OWNER_ID_FK")
    private long ownerID;
    
    // 被动方,关联主控方属性
    @OneToOne(mappedBy = "jvGlobalID")
    private JVSnapshot jvSnapshot;

    // 省略getter/setter
}

JVCommit.java

@Entity
@Table(name = "JV_COMMIT")
public class JVCommit {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jv_commit_pk_seq")
    @SequenceGenerator(name="jv_commit_pk_seq", sequenceName = "JV_COMMIT_PK_SEQ")
    @Column(name= "COMMIT_PK" , insertable = true, updatable = false)
    private long id;

    @Column(name = "AUTHOR")
    private String author;
    
    @Column(name = "COMMIT_DATE")
    private Date commitDate;
    
    @Column(name = "COMMIT_DATE_INSTANT")
    @JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    private String commitDateInstant;
    
    @Column(name="COMMIT_ID")
    private long commitId;  
    
    // 被动方,关联主控方属性
    @OneToOne(mappedBy = "jvCommit")
    private JVSnapshot jvSnapshot;

    // 省略getter/setter
}

2. 修正Service插入逻辑

利用级联保存特性,仅保存主实体即可自动同步关联数据,同时建立双向关联:

// 初始化主实体并设置自身字段
JVSnapshot jvSnapshot = new JVSnapshot();
String existingCustomerIDNew = "somenewString";
String existingCustomerID = "someString";
jvSnapshot.setState(existingCustomerIDNew);
jvSnapshot.setChangedProperties(existingCustomerID);
jvSnapshot.setManagedType("com.dev.neo.models.Customer");
jvSnapshot.setType("UPDATE");
jvSnapshot.setVersion(2);

// 初始化JV_GLOBAL_ID并建立双向关联
JVGlobalID jvGlobalId = new JVGlobalID();
jvGlobalId.setTypeName("com.dev.neo.models.Customer");
jvGlobalId.setLocalID("99999");
jvSnapshot.setJvGlobalID(jvGlobalId);
jvGlobalId.setJvSnapshot(jvSnapshot);

// 初始化JV_COMMIT并建立双向关联
JVCommit jvCommit = new JVCommit();
List<Long> jvCommitObjectList = jvCommitRepository.getMaxCommitId();
Long commitId = jvCommitObjectList.get(0) + 1;
String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
String date = simpleDateFormat.format(new Date());
jvCommit.setAuthor(req.getCheckerId());
jvCommit.setCommitDate(new Date());
jvCommit.setCommitDateInstant(date);
jvCommit.setCommitId(commitId);
jvSnapshot.setJvCommit(jvCommit);
jvCommit.setJvSnapshot(jvSnapshot);

// 级联保存主实体及关联实体
jvSnapshotRepository.save(jvSnapshot);
System.out.println("所有表数据插入完成");

关键说明

  • 删除JVSnapshot中手动维护的globalID、commitID字段,由@JoinColumn自动维护外键,避免手动赋值错误。
  • 使用CascadeType.PERSIST级联保存,确保主实体保存时关联实体同步入库,保证外键关联有效性。
  • 必须同时设置双向关联的双方属性,否则JPA无法正确识别关联关系。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:52:03