Home > Back-end >  JPA Hibernate @ManyToOne foreign key constraint fails
JPA Hibernate @ManyToOne foreign key constraint fails

Time:09-25

I am working on a basic example to test cascade delete operation but I am getting exception. Here is my entities

@Table(name = "project")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Project implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @Column(name = "project_name")
    private String projectName;

    @ManyToOne(cascade = CascadeType.REMOVE)
    private Student student;

    ...
} 

and

@Entity
@Table(name = "student")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Student implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @Column(name = "name")
    private String name;
 
    ...
}

I already have one student data and one project related to it, and my expectation is when I delete student data, that project will automatically be deleted. But when I delete student data, I got an error

Caused by: java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`document_control`.`project`, CONSTRAINT `FKjvi932wdxw4ux6m7u6abiy7qv` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`))

I'm using JPA to delete the data, and here is my controller class

@DeleteMapping("/students/{id}")
    public ResponseEntity<Void> deleteStudent(@PathVariable Long id) {
        log.debug("REST request to delete Student : {}", id);
        studentRepository.deleteById(id);
        return ResponseEntity
            .noContent()
            .headers(HeaderUtil.createEntityDeletionAlert(applicationName, false,
                ENTITY_NAME, id.toString()))
            .build();
    }

Am I missing something here? thanks for the help

CodePudding user response:

The problem resides in that the foreign key from Project to Student is missing the option: "on delete cascade" ... such option instructs the database to remove those Projects that depend on the deleted Student ... if no option is configured, your database will complain with "Constraint violation errors", as the one that you're facing ...

It seems that you're creating your database model through hibernate ... to add such option to the constraint to can follow this example ... The result should be something like this:

@Entity
@Table(name = "project")
public class Project {
    ...
    @ManyToOne
    @JoinColumn(
        name = "student_id", 
        nullable = false,
        foreignKey = @ForeignKey(
            name = "FK_STUDENT_ID",
            foreignKeyDefinition = "FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE"
    )
    private Student student;
    ...
}

NOTE: this is just a quick example, you'll have to test it yourself.

Happy Coding ...

CodePudding user response:

To automatically delete all projects associated with a student on deletion, you could also model the @OneToMany-side like this:

public class Student {
  ...
  @OneToMany(cascade = CascadeType.REMOVE)
  private Set<Projects> projects; // -> student removal will be cascaded to projects by hibernate
  ...
}

(note that specifying @ManyToOne(cascade = CascadeType.REMOVE) in the Project-entity should be done with care, as the deletion of a project would then try to remove an associated student, even though it could still be associated with projects not to be removed - also see: https://thorben-janssen.com/avoid-cascadetype-delete-many-assocations/ for an example )


Another option similar to what Carlitos Way suggested could look like that (see https://stackoverflow.com/a/38495206 ):

public class Project {
  ...
  @ManyToOne
  @OnDelete(action = OnDeleteAction.CASCADE)
  private Student student; // -> hibernate will generate corresponding DDL
  ...
}

CodePudding user response:

Cascading REMOVE operations from the parent to the child will require a relation from the parent to the child (not just the opposite, which seems to be what you currently have). Having said that, try creating a bi-directional relationship as follows:

@Entity
@Table(name = "student")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Student implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @Column(name = "name")
    private String name;
 
    @OneToMany(mappedBy = "student", cascade = CascadeType.REMOVE)
    private Set<Project> projects;
}

Additionally, consider removing the cascading REMOVE operations from Project otherwise you might end up deleting a Student upon deletion of a Project.

  • Related