简体   繁体   中英

Cascade persist JPA entities with unknown PK/FK attributes violates NotNullConstraint

I would like to persist a JPA entity with many 1:1 or 1:many relationships with only one call to persist .

Problem: the entity's primary key is auto generated and used as a foreign key in a child entity. When the transaction is committed, there is an exception pointing out a violated NotNullConstraint on the child entity's foreign key column.

Internal Exception: java.sql.SQLException: ORA-01400: Insertion of NULL in ("SCHEMA"."PROTOCOL_FILE"."PROTOCOL_ID") not possible

Parent entity:

@Entity
@Table(name = "...")
public class Protocol {

    @Id
    @GeneratedValue(generator="SQ_PROTOCOL", strategy=GenerationType.SEQUENCE)
    @SequenceGenerator(name="SQ_PROTOCOL", sequenceName="SQ_PROTOCOL", allocationSize=50)
    @Column(name = "PROTOCOL_ID")
    private Long protocolId;

    @OneToOne(mappedBy="protocol", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private ProtocolFile file;

    //Other attributes and getter/setter omitted
}

Child entity:

@Entity
@Table(name = "PROTOCOL_FILE")
public class ProtocolFile {

    @Id
    @Column(name = "PROTOCOL_ID")
    private Long protocolId;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @JoinColumns(@JoinColumn(name="PROTOCOL_ID", referencedColumnName="PROTOCOL_ID", updatable=false, insertable=false))
    private Protocol protocol;

    //Other attributes and getter/setter omitted
}

Do you know a convenient solution, so I can persist all entities, that belong to Protocol , in one call?

The situation you have here is a "derived identity" of the ProtocolFile - the ID of the ProtocolFile is the ID of the Protocol and there is a one-to-one relationship between them.

I see you are using updatable=false, insertable=false but it's better to follow the specs which suggest to use the @MapsId annotation:

@Entity
@Table(name = "PROTOCOL_FILE")
public class ProtocolFile {

    @Id // No @Column here
    private Long protocolId;

    @MapsId // --- HERE
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @JoinColumn(name="PROTOCOL_ID") // Just that
    private Protocol protocol;
}

Or you may want to skip the protocolId field altogether and put the @Id annotation on the relationship.

@Entity
@Table(name = "PROTOCOL_FILE")
public class ProtocolFile {

    @Id // --- HERE
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @JoinColumn(name="PROTOCOL_ID") // Just that
    private Protocol protocol;
}

Of course, you need to set the protocol instance to the file during creation and not change it later any more (eg. allow to set it only using the ProtocolFile constructor).

See section "2.4.1 Primary Keys Corresponding to Derived Identities" of the JPA 2.0 spec for more details and examples ( Example 4 seems to be your case).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM