一对多单向关系的外键约束问题

时间:2016-03-30 17:03:19

标签: java mysql hibernate spring-mvc foreign-keys

我有Employee(父母)和Emp_Contacts(孩子)。只有Employee类具有单向映射。

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...

我的Emp_Contacts如下:

@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;

....

数据库表对emp_contacts上的emp_id表没有空FK约束。

  1. 如果我删除了上述约束,则persist(employee)将保留员工和相应的emp_contacts。
  2. 使用FK约束我得到以下错误:

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

  3. 我在互联网上搜索并找到了此链接https://forum.hibernate.org/viewtopic.php?f=1&t=995514

    但是,如果我在Employee中加入可空:

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

    我的服务器甚至没有启动,我得到以下错误:

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

    我做错了什么?

3 个答案:

答案 0 :(得分:6)

Employee是拥有方的协会(因为它是唯一的一方)。这样,外键总是与插入关联的Emp_Contacts实例分开更新,因此它必须是可空的。

建议的解决方案是使关联成为双向关联,并使多方成为关联的所有者:

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;
}

这样,外键可以不为空,并且您可以避免额外语句更新外键的成本,因为插入Emp_Contacts时设置了外键值。

此外,您将始终在Emp_Contacts实例中拥有关联的员工ID(似乎是您的意图)。您不必从数据库加载员工以访问其ID,因为可以将关联声明为惰性(如上例所示),Hibernate将生成仅包含id的代理。有关详细信息,请参阅this answer

另一个好处是,您可以在需要时从双方导航关联(在HQL / JPQL查询中也很有用)。

如果您仍然想使用原始解决方案(单方面只与多方的普通外键值关联),那么使连接列可以为空并生成emp_id属性insertable = false, updatable = false,因为Hibernate在Employee端保持关联时将自动更新映射的外键列:

@Column(name = "emp_id, insertable = false, updatable = false")
private long emp_id;

答案 1 :(得分:0)

我不确定这里,但您可以尝试删除

@NotNull
@Column(name = "emp_id")
private long emp_id;
来自班级Emp_Contacts

。对我而言,看起来Hibernate抱怨emp_id的重复映射,并且您已经在Employee中映射了该列。

答案 2 :(得分:0)

  1. 当Hibernate必须删除子值时,它会尝试使外键无效。您不必删除外键上的约束以保持对Emp_Contacts的更改。相反,在JoinColumn注释上,设置updatable = false。这告诉Hibernate不要更改外键值。
  2. 接下来,在OneToMany批注上设置orphanRemoval = true。你会认为Hibernate默认orphanRemoval为true但它没有:(。我们的团队用这种愚蠢的行为进行了几天的斗争。希望这会有所帮助。
相关问题