Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Foreign key constraint issue for One-to-Many unidirectional relation

I have Employee (parent) and Emp_Contacts (child). Only Employee class has unidirectional mapping.

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name="EMPLOYEE")
public class Employee {

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

  @Size(min=3, max=50)
  @Column(name = "NAME", nullable = false)
  private String name;

...

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  @JoinColumn(name = "emp_id")
  private Set<Emp_Contacts> contacts;

...getters and setters...

My Emp_Contacts is as follows:

@Entity
@Table(name = "Emp_Contacts")
public class Emp_Contacts implements Serializable {

  private static final long serialVersionUID = 1L;
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int emp_contact_id;

  @NotNull
  @Column(name = "emp_id")
  private long emp_id;

....

DB table has not null FK constraint for emp_contacts table on emp_id.

  1. If I remove above constraint then persist(employee) will persists employee and corresponding emp_contacts.
  2. With FK constraint I get below error:

    MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails

I searched on internet and found this link https://forum.hibernate.org/viewtopic.php?f=1&t=995514

But if I put nullable in Employee:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "emp_id", nullable = false)
private Set<Emp_Contacts> contacts

my server does not even start, I get below error:

Repeated column in mapping for entity: com.cynosure.model.Emp_Contacts
column: emp_id (should be mapped with insert="false" update="false")

What am I doing wrong ?

like image 954
user2869612 Avatar asked Mar 30 '16 17:03

user2869612


2 Answers

Employee is the association owning side (because it's the only side). That way foreign key is always updated separately from inserting the associated Emp_Contacts instances, so it must be nullable.

The recommended solution is to make the association bidirectional and make the many side the owner of the association:

public class Employee {
  @OneToMany(mappedBy = "employee")
  private Set<Emp_Contacts> contacts;
}

public class Emp_Contacts {
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "emp_id", nullable = false)
  private Employee employee;
}

This way the foreign key can be not-nullable and you avoid the costs of an extra statement to update the foreign key because the foreign key value is set when inserting Emp_Contacts.

Also, you will always have the associated employee id in the Emp_Contacts instance (what seems to be your intention). You don't have to load the employee from the database in order to access its id because the association can be declared lazy (as in the example above) and Hibernate will generate a proxy containing only the id. See this answer for more information.

Another benefit is that you can navigate the association from both sides when needed (useful also in HQL/JPQL queries).

If you nevertheless want to use your original solution (unidirectional association with only plain foreign key value on the many side), then make the join column nullable and make the emp_id property insertable = false, updatable = false, because Hibernate will automatically update the mapped foreign key column when maintaining the association on the Employee side:

@Column(name = "emp_id, insertable = false, updatable = false")
private long emp_id;
like image 124
Dragan Bozanovic Avatar answered Nov 03 '22 00:11

Dragan Bozanovic


  1. When Hibernate has to delete a child value, it tries to nullify the foreign key. You should not have to remove the constraint on the Foreign Key to persist changes to Emp_Contacts. Instead, on the JoinColumn annotation, set updatable = false. This tells Hibernate to NOT change foreign key values.
  2. Next, set orphanRemoval = true on the OneToMany annotation. You would think Hibernate defaults orphanRemoval to true but it does not :(. Our team fought with this silly behavior for a couple days. Hope this helps.
like image 34
crazyDiamond Avatar answered Nov 03 '22 01:11

crazyDiamond