Reputation: 421
I have this relationship structure in database, that I cannot change:
------------------------
| Person |
------------------------
id : pk
------------------------
------------------------
| PersonAddress |
------------------------
person_id: pk, fk
address_id: pk, fk
address_type
------------------------
------------------------
| Address |
------------------------
id: pk
------------------------
I am trying to configure these relations with JPA and Hibernate annotations. One of the ways I've tried to do was this one:
@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;
}
When i do repository.save(person)
. I expect that changes would cascade down to the address.
The problem is that, when I try to save, I get NullPointerException. While debugging I found out, that Hibernate tries to save PersonAddress object, and it needs hash code of one of the primary keys it uses.
Since Address is not yet save, it has id == null. Basically the flow of save is currently:
Person -> PersonAddress -> Address
But it should be:
Person -> Address -> PersonAddress
(because it needs to get generated ids for person and adddress).
Is there a way to configure Hibernate to behave in that way?
Upvotes: 2
Views: 1785
Reputation: 421
Found out the problem. When creating @Embeddable
class for primary key usage, you need to provide simple objects for id columns. I.e. it should not contain objects mapped by foreign key:
@Embeddable
class PersonAddressId implements Serializable {
@Column(nullable = false)
private Long relatedPartyId;
@Column(nul
private Long addressId;
}
To support composite primary key, as well as foreign keys, you need to add @MapsId
annotation:
@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;
}
Upvotes: 1
Reputation: 36223
Your hashCode method is wrong implemented. You must always check for null because this method can be called in various situations.
Read more about that topic: https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
Upvotes: 0