I'm trying to use orphanRemoval in Hibernate 4.3.5 / JPA2 objects but it does not seem to be working as I expected. I am not sure, however, if I am doing something incorrect, or if this is still a bug in Hibernate.
Given the following relationships (@Version, getters and setters omitted for brevity):
@Entity
public class Provider implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
private String name;
@OneToMany(orphanRemoval=true,cascade=CascadeType.REMOVE)
@JoinColumn(name="provider_id", referencedColumnName="id")
private List<Contract> contracts;
}
@Entity
public class Contract implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
private String volume;
@OneToMany(orphanRemoval=true,cascade=CascadeType.REMOVE) // delete any attachments that were previously uploaded with this contract
@JoinTable(name="contract_attachment", joinColumns = @JoinColumn(name = "contract_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "attachment_id", referencedColumnName = "id"))
private List<Attachment> attachments;
}
@Entity
public class Attachment implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
private String filename;
}
I would expect that if I remove a contract from the Provider.contracts list, that it would delete corresponding rows from the contract table and all associated attachments from the attachment table. However, only the contract table gets deleted. The attachment table is not modified.
Ex:
// loop over all contracts and delete the one with the matching id
for(Iterator<Contract> it = provider.getContracts().iterator(); it.hasNext();){
Contract c = it.next();
if( c.getId() == contractId ){
it.remove();
break;
}
}
Given that the attachments are ManyToOne relative to the Contract table, if the Contract is deleted, then the attachments are orphaned. But even with the orphanRemoval=true
, this does not delete the rows from the DB.
I found several issues relating to this for Hibernate 3 (both here on SO, and Jira and elsewhere online), but I had understood that it was fixed in Hibernate 4. But using Hibernate 4.3.5 I am still seeing this issue. From this issue, it seems to work, so I am not sure why I cannot get it functional.
Is there something wrong/missing in my code, or is Hibernate still problematic? Am I required to implement equals
and hashCode
in any of these entity classes for orphanRemoval
to work properly? I tried implementing both methods in Contract and Attachment, but has made no difference.
Looking at the Hibernate logs, it shows Hibernate making the changes to the join table (or FK mapping), but does not actually delete the row from the associated table. I can see Hibernate setting the provider_id=null in the Contract table, but shouldn't it be deleting the Contract row instead?
2014-07-04 15:06:41,333 [main] [-] DEBUG org.hibernate.SQL -
/* update
com.ia.domain.Provider */ update
provider
set
default_contact_id=?,
name=?,
type=?,
version=?,
website=?
where
id=?
and version=?
Hibernate:
/* update
com.ia.domain.Provider */ update
provider
set
default_contact_id=?,
name=?,
type=?,
version=?,
website=?
where
id=?
and version=?
2014-07-04 15:06:41,334 [main] [-] TRACE hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [null]
2014-07-04 15:06:41,334 [main] [-] TRACE hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - [name_3]
2014-07-04 15:06:41,335 [main] [-] TRACE org.hibernate.type.EnumType - Binding [CARRIER] to parameter: [3]
2014-07-04 15:06:41,336 [main] [-] TRACE hibernate.type.descriptor.sql.BasicBinder - binding parameter [4] as [INTEGER] - [2]
2014-07-04 15:06:41,336 [main] [-] TRACE hibernate.type.descriptor.sql.BasicBinder - binding parameter [5] as [VARCHAR] - [website_3]
2014-07-04 15:06:41,337 [main] [-] TRACE hibernate.type.descriptor.sql.BasicBinder - binding parameter [6] as [BIGINT] - [4]
2014-07-04 15:06:41,338 [main] [-] TRACE hibernate.type.descriptor.sql.BasicBinder - binding parameter [7] as [INTEGER] - [1]
2014-07-04 15:06:41,342 [main] [-] DEBUG org.hibernate.SQL -
/* delete one-to-many com.ia.domain.Provider.contracts */ update
contract
set
provider_id=null
where
provider_id=?
Hibernate:
/* delete one-to-many com.ia.domain.Provider.contracts */ update
contract
set
provider_id=null
where
provider_id=?
2014-07-04 15:06:41,344 [main] [-] TRACE hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [4]
Honestly, I don't know why, but if you add CascadeType.PERSIST
(or better CascadeType.ALL
) to your @OneToMany
relationship in Provider
entity it will work as expected.
Probably the Hibernate documentation is lacking in this little detail.
update EclipseLink 2.5.1 with JPA2 does not seem to have this issue
2nd update
In Section 2.9, Entity Relationships, the JPA 2.1 spec says: "If the entity being orphaned is a detached, new, or removed entity, the semantics of orphanRemoval do not apply."
I don't know if your related entities are detached, but if yes then it's not a bug :)