简体   繁体   中英

How to achieve Foreign key constraint in JPA + Hibernate

It's may day 5 with Hibernate. I'm learning JPA. My question is very much similar to:

JPA + Hibernate: How to define a constraint having ON DELETE CASCADE

But the problem is:

  1. It was asked 8 years ago. So the versions will not match today.
  2. The OP didn't mention the version of Hibernate or MySQL or Java.
  3. There is no accepted answer.

I've two tables student and guide . They are linked with a foreign key: 在此处输入图像描述

I didn't create these tables. Hibernate created them for me because I'm using these annotations in my student class:

@ManyToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
@JoinColumn(name="guide_id")
private Guide guide;

It is clearly seen that Raj and Tanzeel have same guide ie Joe . I want to maintain the referential integrity constraint. That means If I delete Raj then Joe will also get deleted. That means Tanzeel will be left without any guide. That should not happen right? But this is what I'm facing.

Here is my code:

Main.java

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction txn = session.getTransaction();
        try {
            txn.begin();
            // deleting Student[id=2L] (using CascadeType.REMOVE)
            Student student = (Student) session.get(Student.class, 2L);
            session.delete(student);
            txn.commit();
        }   catch(Exception e) {
                ...
        }
    }
}

Student.java

@Entity
public class Student {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    
    @Column(name="enrollment_id", nullable=false)
    private String enrollmentId;    
    
    private String name;
    
    @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
    @JoinColumn(name="guide_id")
    private Guide guide;
    
    public Student() {}
    public Student(String enrollmentId, String name, Guide guide) {
        this.enrollmentId = enrollmentId;
        this.name = name;
        this.guide = guide;
    }
    ...
}

Guide.java

@Entity
public class Guide {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    
    @Column(name = "staff_id", nullable = false)
    private String staffId;
    private String name;
    private Integer salary;

    public Guide() {}
    public Guide(String staffId, String name, Integer salary) {
        this.staffId = staffId;
        this.name = name;
        this.salary = salary;
    }
    
}

Some info from my hibernate.cfg.xml

<session-factory>
    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">true</property>
        
    <property name="hbm2ddl.auto">update</property>
        
    <!-- Use Annotation-based mapping metadata -->
    <mapping class="entity.Student"/>
    <mapping class="entity.Guide"/>
</session-factory>

The problem is, when i deleted Raj , his guide Joe also got deleted and there was no exception seen regarding foreign key violation. Guide table has only Mike now and Tanzeel is referring to the guide Joe who doesn't even exists now.

My versions:

  1. Java 11
  2. mysql-connector-java 8.0.26
  3. hibernate-core 5.5.6.Final

Please pitch in.

in your case, I would have used the bidirectional association instead of the unidirectional one.

So you're Guide entity would have seen like this,

@Entity
public class Guide {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    
    @OneToMany(mappedBy="guide")
    private List<Student> students;    

    @Column(name = "staff_id", nullable = false)
    private String staffId;
    private String name;
    private Integer salary;

    public Guide() {}
    public Guide(String staffId, String name, Integer salary) {
        this.staffId = staffId;
        this.name = name;
        this.salary = salary;
    }

}

I suggest you take a look at other examples on this page .

Hope it could help you.

-- Edited --

I suggest you check this link , Thorben clarifies, why you shouldn't use CascadeType.REMOVE for -ToMany associations. In the end, you should remove the entity yourself using EntityManager or remove method.

And the above entity change of mine is irrelevant. Ignore it please.

CascadeType.REMOVE: It means that the related entities are deleted when the owning entity is deleted.

If you only have it mapped unidirectionally (Student to Guide), and you eliminate in cascade it is logical the behavior you are receiving.

Don't use cascade.REMOVE and add the other relation (Guide to Student) I think it would be the expected behavior.

@Entity
public class Student {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    
    @Column(name="enrollment_id", nullable=false)
    private String enrollmentId;    
    
    private String name;
    
    @ManyToOne()
    @JoinColumn(name="guide_id")
    private Guide guide;

}


@Entity
public class Guide {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    
    @Column(name = "staff_id", nullable = false)
    private String staffId;
    private String name;
    private Integer salary;

   @OneToMany(mappedBy="guide")
    private List<Student> students; 
    
}

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