Reputation: 3056
I am trying to map the following code: * Order contains many OrderItems * Article can be referenced by (contains) many OrderItems
In one transaction I need to create Order, several Articles and OrderItems with the new articles. All is clear when there is straigth path - Order contains OrderItems and so on - then I can call
orderRepository.save(order);
orderRepository.flush();
New order entity with the OrderItems entities is saved. The problem begins with trying to save Article entities as well. Apparently more work is required, because I am getting the error message;
org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing
How to organize such work in one transaction? Do multiple repositories can work under single transaction and if so - whose flush() completes the transaction. Are there necessary multiple flushe's (e.g. one for article entities and another for order) and if so - how to rollback all of them, if necessary?
Apparently it is not possible to mix repositories with the simple EntityManager.getTransaction().{begin(), commit()}, code because then the error message is:
java.lang.IllegalStateException: Cannot obtain local EntityTransaction from a transaction-synchronized EntityManager
What is the best practice in case where one entity (OrderItem) is owned by two entities (Order and Article) and all three entities should be created in one transaction?
Partial code added. completeOrder is the test procedure, where 5 items and 5 new articles are created for new order. This procedure gives exception "references and unsaved transient instance". I am using composite key for Article entity, but this should not be an issue, I am trying now the same scenario with Article entity that has usual single-field key.
@Entity
public class Order extends FrameworkEntity {
...
@OneToMany(mappedBy="order", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<OrderItem>();
}
@Entity
public class OrderItem extends FrameworkEntity {
...
@ManyToOne
@JoinColumn(name="fk_order")
private Order order;
@ManyToOne
@JoinColumns({
@JoinColumn(name="fk_date"),
@JoinColumn(name="fk_number")
})
private Article article;
}
@Entity
public class Article extends FrameworkEntity {
...
@Id
protected PkArticle pkArticle;
@OneToMany(mappedBy="article", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<OrderItem>();
}
@Embeddable
public class PkArticle extends FrameworkCompositeKey {
@Column(name = "article_number")
private Long articleNumber;
@Temporal(TemporalType.DATE)
@Column(name = "article_date")
private Date articleDate;
...
}
public class OrderService {
public void completeOrder() {
Order entity = new Order();
for (int i=0; i<5; i++) {
Date articleDate;
Long articleNumber;
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(0);
cal.set(2015, 1, 31, 0, 0, 0);
articleDate = cal.getTime();
articleNumber = (new Random()).nextLong();
Article article = new Article(articleNumber, articleDate);
//entityManager.getTransaction().begin();
//entityManager.persist(space);
//entityManager.getTransaction().commit();
OrderItem item = new OrderItem();
item.setArticle(article);
article.getOrderItems().add(item);
item.setOrder(entity);
entity.getOrderItems().add(item);
}
entity=(Order) repository.save(entity);
repository.flush();
}
]
Upvotes: 2
Views: 3924
Reputation: 20135
Do you have code like this?
@Entity
public class OrderItem {
@ManyToOne
private Article article;
}
If yes, change this to:
@Entity
public class OrderItem {
@ManyToOne(cascade = CascadeType.PERSIST)
private Article article;
}
This will ensure that when a new OrderItem
with a yet-to-be-saved Article
is persisted, the Article
will be saved as well.
Upvotes: 5