Issue
public class ClientEntity {
@Id
@Column(name="id", nullable = false, unique = true)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sqs_clients")
@SequenceGenerator(name = "sqs_clients", sequenceName = "sqs_clients", allocationSize = 1)
private Long id;
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
Set<ParcelEntity> parcels;
public void addParcel(ParcelEntity parcel) {
if (parcel != null){
if (parcels == null) {
parcels = new HashSet<>();
}
parcels.add(parcel);
}
}
}
public class ParcelEntity {
@Id
@Column(name="id", nullable = false, unique = true)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sqs_parcels")
@SequenceGenerator(name = "sqs_parcels", sequenceName = "sqs_parcels", allocationSize = 1)
private Long id;
@ManyToOne
@JoinColumn(name = "client_id", nullable = false)
private ClientEntity client;
}
@Override
public void createOrder(Long clientId, CreateParcelReq createParcelReq) {
clientRepo.findById(clientId).ifPresentOrElse(client-> {
client.addParcel(parcelMapper.dtoToEntity(createParcelReq));
clientRepo.save(client);
}, ()->{throw new RuntimeException();});
}
I have created manyToOne relationship between parcel and client tables. Parcel entity is saved, but foreign key (clientId) is set to null in table. How can I fix it?
Solution
In the case of a bidirectional relationship Hibernate (or JPA implementations) cares only about the owning side of the association. The owning side is the side that doesn't have the mappedBy
attribute.
So if we only call clientEntity.addParcel(parcelEntity)
, the foreign key will NOT be linked to the new ParcelEntity
, because this is not the owning /tracked side of the relation.
You need to explicitly call parcelEntity.setClient(clientEntity)
, because that is the owning side of the relation.
When using mappedBy
, it is the responsibility of the developer to know what is the owning side, and update the correct side of the relation in order to trigger the persistence of the new relation in the database.
See documentation and also JPA Specification "2.9 Entity Relationships"
If the relationship is bidirectional, the mappedBy element must be used to specify the relationship field or property of the entity that is the owner of the relationship.
Correct implementation will be:
@Override
public void createOrder(Long clientId, CreateParcelReq createParcelReq) {
clientRepo.findById(clientId).ifPresentOrElse(client-> {
ParcelEntity parcelEntity = parcelMapper.dtoToEntity(createParcelReq);
parcelEntity.setClient(client);
client.addParcel(parcelEntity);
clientRepo.save(client);
}, ()->{throw new RuntimeException();});
}
Answered By - Eugene
Answer Checked By - Gilberto Lyons (JavaFixing Admin)