Reputation: 3038
I have the following entity relationship:
@Entity
@Table(name="order_info")
public class OrderInfo{
@OneToMany(mappedBy="orderInfo",fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@JsonIgnore
private List<OrderItem> orderItems;
}
@Entity
@Table(name="reason_codes")
public class ReasonCode{
//bi-directional many-to-one association to OrderItem
@JsonIgnore
@OneToMany(mappedBy="reasonCode",fetch=FetchType.EAGER,cascade=CascadeType.ALL)
private List<OrderItem> orderItems;
}
@Entity
@Table(name="order_item")
public class OrderItem{
//bi-directional many-to-one association to OrderInfo
@ManyToOne
@JoinColumn(name="order_info_id", nullable=false)
private OrderInfo orderInfo;
//bi-directional many-to-one association to ReasonCode
@ManyToOne
@JoinColumn(name="reason_code_id", nullable=false)
private ReasonCode reasonCode;
}
Each OrderInfo has many OrderItem, each ReasonCode can be mapped to multiple OrderItems, and each OrderItem MUST have 1 OrderInfo, and 1 ReasonCode.
And finally, the code to set everything up:
class Test{
...
List<OrderItem> orderItems = new ArrayList<OrderItem>();
for(...){
OrderItem item = new OrderItem();
ReasonCode rrCode = rcRepostory.findOne("2");
item.setReasonCode(rrCode);
orderItems.add(item);
}
OrderInfo order = CreateOrder.create(orderItems);
orderInfoRepo.save(order);
}
class CreateOrder{
public static OrderInfo create(List<OrderItem> orderItems){
OrderInfo oInfo = new OrderInfo();
oInfo. setABC(...);
for(OrderItem item : orderItems){
item.setOrderInfo(oInfo);
}
oInfo.setOrderItems(orderItems);
return oInfo;
}
}
Now, when I try to save the orderInfo entity, I get the following error:
Not-null property references a transient value - transient instance must be saved before current operation : model.OrderItem.reasonCode -> model.ReasonCode
Basically, what I wan to do is, when I save OrderInfo, I want the OrderItems to be saved as well.
From the many posts that I saw online, adding the fetch as eager, and cascade on the many-to-one mapping should resolve this. However, I still face the issue even after adding them.
The ReasonCode already exists in the database. What is the problem here?
Thanks.
Upvotes: 0
Views: 1516
Reputation: 385
You are using bidirectional mapping between two entities OrderItem and OrderInfo.
In your example, the owning entity is the OrderItem.
You may read this one to understand better:
What is the "owning side" in an ORM mapping?
Here an example with employee/department(OrderItem/OrderInfo) with department as owning side:
http://examples.javacodegeeks.com/enterprise-java/jpa/one-to-many-bidirectional-mapping-in-jpa/
So if you want to go on with your mapping you should save your OrderItem this way:
OrderInfo orderInfo =oiRepostory.findOne("2");
for(...){
OrderItem item = new OrderItem();
ReasonCode rrCode = rcRepostory.findOne("2");
item.setReasonCode(rrCode);
item.setOrderInfo(orderInfo);
orderItemRepo.save(item);
}
Using mappedBy, If we only call orderInfo.getOrderItems().add(orderItem), the foreign key in order_item will NOT be linked to the new orderInfo, because this is not the owning /tracked side of the relation!
To link the orderItem to the new orderInfo, you need to explicitly call orderItem.setOrderInfo(orderInfo), because that is the owning side of the relation. BTW you are doing it right with item.setReasonCode(rrCode);
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.
Cascade option also won't work in the opposite direction.
I hope this will make it more clear. Anyway, you need to understand the owning side concept to go on.
Upvotes: 1