It's may day 5 with Hibernate. I'm learning JPA. My question is very much similar to:
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 i.e 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:
- Java 11
- mysql-connector-java 8.0.26
- hibernate-core 5.5.6.Final
Please pitch in.
CodePudding user response:
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.
CodePudding user response:
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;
}