Hibernate JPA cascade delete issue
For explaning my issue, I have created this simple project with two tables : item and order1 (with item having orderId as Foreign Key).
Now when I try to delete a开发者_开发百科 row from order table, i get error saying that particular order id is linked with few items in 'item' table.
So, what i do is i iterate over all the item objects (using 'Eager' loading) , and call destroy() for each of them. And after deleteing all the items , I call order.destroy(). But now, I get the following error:
Exception in thread "main" javax.persistence.RollbackException: Error while commiting the transaction
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
at com.sourind.test.testorm.controller.ItemJpaController.destroy(ItemJpaController.java:112)
at com.sourind.test.testorm.App.main(App.java:31)
Caused by: org.hibernate.ObjectDeletedException: deleted entity passed to persist: [com.sourind.test.testorm.entity.Item#<null>]
I am using Hibernate(JPA 2.0 ). Any help in this matter would be greatly appreciated.
For reference, I am copying the entity classes created by netbeans:
@Entity
@Table(name = "item", catalog = "test", schema = "")
@NamedQueries({
@NamedQuery(name = "Item.findAll", query = "SELECT i FROM Item i"),
@NamedQuery(name = "Item.findById", query = "SELECT i FROM Item i WHERE i.id = :id"),
@NamedQuery(name = "Item.findByName", query = "SELECT i FROM Item i WHERE i.name = :name")})
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Basic(optional = false)
@Column(name = "name")
private String name;
@JoinColumn(name = "orderId", referencedColumnName = "id")
@ManyToOne(optional = false)
private Order1 order1;
public Item() {
}
public Item(Integer id) {
this.id = id;
}
public Item(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Order1 getOrder1() {
return order1;
}
public void setOrder1(Order1 order1) {
this.order1 = order1;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Item)) {
return false;
}
Item other = (Item) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.sourind.test.testorm.entity.Item[id=" + id + "]";
}
}
And the order1 class:
@Entity
@Table(name = "order", catalog = "test", schema = "")
@NamedQueries({
@NamedQuery(name = "Order1.findAll", query = "SELECT o FROM Order1 o"),
@NamedQuery(name = "Order1.findById", query = "SELECT o FROM Order1 o WHERE o.id = :id"),
@NamedQuery(name = "Order1.findByOrderDate", query = "SELECT o FROM Order1 o WHERE o.orderDate = :orderDate")})
public class Order1 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Basic(optional = false)
@Column(name = "orderDate")
@Temporal(TemporalType.TIMESTAMP)
private Date orderDate;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order1", fetch=FetchType.EAGER)
private List<Item> itemList;
public Order1() {
}
public Order1(Integer id) {
this.id = id;
}
public Order1(Integer id, Date orderDate) {
this.id = id;
this.orderDate = orderDate;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public List<Item> getItemList() {
return itemList;
}
public void setItemList(List<Item> itemList) {
this.itemList = itemList;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Order1)) {
return false;
}
Order1 other = (Order1) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.sourind.test.testorm.entity.Order1[id=" + id + "]";
}
}
Thanks in advance, Souri
@Alex Here is the code for ItemJpaController#destroy()
public void destroy(Integer id) throws NonexistentEntityException {
EntityManager em = null;
try {
em = getEntityManager();
em.getTransaction().begin();
Item item;
try {
item = em.getReference(Item.class, id);
item.getId();
} catch (EntityNotFoundException enfe) {
throw new NonexistentEntityException("The item with id " + id + " no longer exists.", enfe);
}
Order1 order1 = item.getOrder1();
if (order1 != null) {
order1.getItemList().remove(item);
order1 = em.merge(order1);
}
em.remove(item);
em.getTransaction().commit();
} finally {
if (em != null) {
em.close();
}
}
}
And also, the version of hibernate I am using did not have DELETE_ORPHAN
cascade type.
Also, how do I get session? and should I get it in the JpaController
class inside the destroy()
method or in the mail method from which I am creating/deleting objects.?
You should just be able to delete the Order record and have it delete all of the associated Item records. To do this you need to add another cascade type to the association between the Order and the Items.
@OneToMany(cascade=CascadeType.ALL, mappedBy = "order1", fetch=FetchType.EAGER, orphanRemoval=true)
private List<Item> itemList;
This tells hibernate that when you delete the parent Order it should delete the child Item records.
session.delete(order);
This delete will cascade down to the child Item records and remove them.
精彩评论