views:

1352

answers:

3

How do you get a OneToOne item to automatically remove with JPA/Hibernate? I would expect simply setting the OneToOne item to be null in the class that contains would be smart enough to allow Hibernate to delete it.

Given a simple object, simplified:

@Entity
public class Container {
    private Item item;

    @OneToOne(cascade=CascadeType.ALL)
    public Item getItem() { return item; }

    public void setItem(Item newItem) { item = newItem; }
}

When an Item is set on Container an Container is persisted with merge a row gets inserted.

Container container = new Container();
container.setItem(new Item());
container = entityManager.merge(container);
// Row count is 1

But when the item is set null, or to another item, the old object still exists in the table.

container.setItem(null);
container = entityManager.merge(container);
// Row count is STILL 1, leaving orphaned rows.

So, how do I remove these OneToOne orphans?

A: 

Try to change to

@OneToOne
@Cascade(cascade = {CascadeType.ALL, CascadeType.DELETE_ORPHAN})

See also my answer on similar post here.

FoxyBOA
Works fine, thank you. I am okay with using Hibernate-specific annotations until JPA 2.0.
Sheldon Young
Ignore that, updated test reveals the approach above does *not* work. According to Hibernate team member Emmanuel delete-orphan is not supposed to work with OneToOne http://forum.hibernate.org/viewtopic.php?p=2287278
Sheldon Young
A: 

Unfortunately, there is no way to do this in JPA without tying yourself to Hibernate's implementation.

So yes, as Foxy says, you can use org.hibernate.annotations.CascadeType instead of the standard JPA annotation, which allows you to specify DELETE_ORPHAN. If you want to use the JPA abstraction, you must delete orphans yourself as of yet.

Henning
+1  A: 

I'm guessing that the reasone behind hibernate not allowing DELETE_ORPHAN to OneToOne relations is related to this issue.

If you really want this bad, you can hack your way with these steps:

  • transform your OneToOne relation into a OneToMany:
  • add a method just to get the first element of the child's collection (this is optional but handy)
  • use the *DELETE_ORPHAN* annotation

Of course, this is a big hack.

Miguel Ping