Reputation: 2029
I am trying to map a bi-directional (one to many and many to one) relationship in Hibernate. We're getting errors when attempting save a result.
The error we're getting is:
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value : com.example.Component.resultId at org.hibernate.engine.internal.Nullability.checkNullability(Nullability.java:92)
From the source code below a Result
can have many Component
s and many Component
s can belong to a single Result
. One of the requirement is that the resultId
in Result
must be a String
. This requirement is out of our control.
An example Entity source code is below:
@Entity
@Table(name = "result")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Result extends AbstractEntity {
@OneToMany(cascade = CascadeType.MERGE)
@JoinColumn(name = "resultId")
private List<Component> component = new ArrayList<>();
}
@Entity
@Table(name = "cmpt")
@XmlRootElement
public class Component extends AbstractEntity {
@ManyToOne(targetEntity = Result.class)
private String resultId;
}
Here is the solution that worked for me
@Entity
@Table(name = "result")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Result extends AbstractEntity {
@OneToMany(cascade = CascadeType.MERGE, nullable = false)
@JoinColumn(name = "resultId")
private List<Component> component = new ArrayList<>();
}
@Entity
@Table(name = "cmpt")
@XmlRootElement
public class Component extends AbstractEntity {
@Column(name = "result_id", insertable = false, updatable = false, nullable = false)
private String resultId;
}
Upvotes: 0
Views: 829
Reputation: 2029
Here is the solution that worked for me:
@Entity
@Table(name = "result")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Result extends AbstractEntity {
@OneToMany(cascade = CascadeType.MERGE, nullable = false)
@JoinColumn(name = "resultId")
private List<Component> component = new ArrayList<>();
}
@Entity
@Table(name = "cmpt")
@XmlRootElement
public class Component extends AbstractEntity {
@Column(name = "result_id", insertable = false, updatable = false, nullable = false)
private String resultId;
}
Upvotes: 0
Reputation: 2477
The correct way to create a bidirectional entity is this.
@Entity
@Table(name = "result")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Result extends AbstractEntity {
@OneToMany(cascade = CascadeType.MERGE, mappedBy="result")
private List<Component> component = new ArrayList<>();
}
@Entity
@Table(name = "cmpt")
@XmlRootElement
public class Component extends AbstractEntity {
@ManyToOne(targetEntity = Result.class)
@JoinColumn(name = "result_id") //result_id is the column name (foreign key) in cmpt table
private Result result;
@Column(name = "result_id", insertable = false, updatable = false)
private String resultId;
}
This will ensure there will be only one bi-direction relation (not two uni-directional relations etc). Let me know if you are expecting something else.
Update 1:
You can add another key to the entity as specified above. You'll get the foreign key as a string. But you need to remember that the actual mapping with JoinColumn should not be removed. I have tested the above configuration and it seems to work for me.
Upvotes: 1
Reputation: 376
I think you have to remove the annotation @ManyToOne(targetEntity = Result.class)
from Component.java class and when you tries to save data first you have to save Result using save method and then you got the resultId returned by save method set this resultId in component entity using setter and getter and then save the component
Upvotes: 0
Reputation: 26572
You have to declare an entity as the field annotated with @ManyToOne. Also i think in your case you should put the @Join column on the Component side:
@Entity
@Table(name = "cmpt")
@XmlRootElement
public class Component extends AbstractEntity {
@ManyToOne
@JoinColumn(name = "resultId")
private Result result;
}
now you dont need the targetEntity
attrbute.
Also remove the @JoinColumn
from @OneToMany
and add mappedBy
attribute:
@Entity
@Table(name = "result")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Result extends AbstractEntity {
@OneToMany(cascade = CascadeType.MERGE, mappedBy="result")
private List<Component> component = new ArrayList<>();
}
Upvotes: 0