使用复合键的级联实体保存抛出NullPointerException

时间:2018-01-18 15:12:07

标签: java hibernate jpa spring-data

我在数据库中有这种关系结构,我无法改变:

------------------------
| Person               | 
------------------------
 id : pk              
------------------------

------------------------
| PersonAddress        | 
------------------------
 person_id: pk, fk    
 address_id: pk, fk
 address_type 
------------------------

------------------------
| Address              | 
------------------------
 id: pk    
------------------------

我正在尝试使用JPA和Hibernate注释配置这些关系。 我尝试过的方法之一就是这个:

@Entity
class Person {

  @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
  @JoinColumn(name = "person_id")
  private List<PersonAddress> addresses;
}

@Entity
class PersonAddress {

  @ManyToOne
  @JoinColumn(nullable = false)
  private AddressType type;

  @EmbeddedId
  private PersonAddressId id;
}


@Embeddable
class PersonAddressId implements Serializable {

  @ManyToOne
  @JoinColumn(name = "person_id")
  private Person person;

  @ManyToOne(cascade = CascadeType.ALL)
  @MapsId("id")
  @JoinColumn(name = "address_id", nullable = false)
  private Address address;

}

@Entity
public class Address {

  @Id
  @GeneratedValue
  @Column(name = "address_id")
  private Long id;
}

当我做repository.save(person)时。我希望更改会级联到地址。

问题是,当我尝试保存时,我得到NullPointerException。调试时我发现,Hibernate试图保存PersonAddress对象,它需要使用其中一个主键的哈希码。

由于Address尚未保存,因此它具有id == null。目前基本上是保存流程:

Person -> PersonAddress -> Address

但它应该是:

Person -> Address -> PersonAddress

(因为它需要为person和adddress生成id)。

有没有办法让Hibernate以这种方式运行?

2 个答案:

答案 0 :(得分:0)

你的hashCode方法实现错误。您必须始终检查null,因为可以在各种情况下调用此方法。

详细了解该主题:https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/

答案 1 :(得分:0)

发现问题所在。为主键使用创建@Embeddable类时,需要为id列提供简单对象。即它不应包含由外键映射的对象:

@Embeddable
class PersonAddressId implements Serializable {

@Column(nullable = false)
private Long relatedPartyId;

@Column(nul
private Long addressId;

}

要支持复合主键以及外键,您需要添加@MapsId注释:

@Entity
public class Person Address {

  @ManyToOne
  @MapsId("personId")
  @JoinColumn(name = "person_id", nullable = false, updatable = false)
  private RelatedParty relatedParty;

  @EmbeddedId
  @JsonIgnore
  private RelatedPartyAddressId id;

  @ManyToOne
  @MapsId("addressId")
  @JoinColumn(name = "address_id", nullable = false)
  private Address address;

}