Hibernate violates referential integrity of an underlying database
------------------------------------------------------------------
Key: HHH-2090
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2090
Project: Hibernate3
Type: Bug
Components: core
Versions: 3.2.0.cr2
Environment: 3.2.CR2
Reporter: Andrei Iltchenko
Priority: Critical
To my surprise I discovered that Hibernate can violate the referential integrity of an
underlying relational database when dealing with unidirectional associations. It may leave
a foreign key un-nullified when the referred entity is deleted, whereby leading to data
corruption.
I used a very simple scenario to discover the problem. Two domain classes ClassA (*)
--> ClassB(0..1) each having a single string attribute. The classes are mapped to
POJOs:
public class ClassA implements Serializable {
/** Primary key attribute: id. */
private String id;
public String getId() {...}
public void setId(String value) {...}
/** Locking attribute: version. */
private Integer version;
public Integer getVersion() {...}
public void setVersion(Integer value) {...}
/** Regular attribute: nameA. */
private String nameA;
public String getNameA() {...}
public void setNameA(String value) {...}
/** Reference: classB. */
private ClassB classB;
public ClassB getClassB() {...}
public void setClassB(ClassB value) {...}
...
}
public class ClassB implements Serializable {
/** Primary key attribute: id. */
private String id;
public String getId() {...}
public void setId(String value) {...}
/** Locking attribute: version. */
private Integer version;
public Integer getVersion() {...}
public void setVersion(Integer value) {...}
/** Regular attribute: nameB. */
private String nameB;
public String getNameB() {...}
public void setNameB(String value) {...}
}
And the following mapping files are used:
ClassA:
<hibernate-mapping schema="rootpackage">
<class name="ClassA"
table="ClassA" optimistic-lock="version"
mutable="true">
<!-- Identifier (primary key) attribute. -->
<id name="id" column="uniqueId" type="string">
<generator class="assigned"/>
</id>
<!-- Locking attribute. -->
<version name="version" column="version_column"
type="integer"/>
<!-- Regular attributes. -->
<property name="nameA" type="string">
<column name="nameA"/>
</property>
<!-- Uni-directional many-to-one association to entity: ClassB. -->
<many-to-one name="classB" class="ClassB"
cascade="all">
<column name="ClassBUniqueId"/>
</many-to-one>
</class>
</hibernate-mapping>
ClassB:
<hibernate-mapping schema="rootpackage">
<class
name="ClassB"
table="ClassB" optimistic-lock="version"
mutable="true">
<!-- Identifier (primary key) attribute. -->
<id name="id" column="uniqueId" type="string">
<generator class="assigned"/>
</id>
<!-- Locking attribute. -->
<version name="version" column="version_column"
type="integer"/>
<!-- Regular attributes. -->
<property name="nameB" type="string">
<column name="nameB"/>
</property>
</class>
</hibernate-mapping>
If I create one ClassA POJO instance associated with a ClassB POJO and persist the
changes, I get the following in my database tables:
ClassA
CLASSBUNIQUEID VERSION_COLUMN UNIQUEID NAMEA
1 0 1 name1a
ClassB
VERSION_COLUMN UNIQUEID NAMEB
0 1 name1b
If I then start a new Session, get the ClassB POJO:
pojo = (ClassB) session.get(ClassB.class, "1");
and delete it:
session.delete(pojo);
tx.commit();
session.close();
I see that the ClassB record has correctly been removed from the table, but the
corresponding ClassA record still hold the foreign key to the non-existent ClassB entity.
ClassA
CLASSBUNIQUEID VERSION_COLUMN UNIQUEID NAMEA
1 0 1 name1a
Yes, a ClassB POJO has no reference to a ClassA POJO and Hibernate uses transitive
persistence, but Hibernate has all necessary information in the mapping files to see that
when deleting a ClassB entity, the other end's FK must be nullified. It must ensure
that database data corruption never occurs as a result of its operation. The only
"remedy" I discovered against the problem is the not-found="ignore"
attribute of the many-to-one element. But this is merely trying to sweep the problem under
the carpet rather than dealing with it.
I am genuinely surprised at Hibernate not being able to correctly and transparently handle
unidirectional associations in this version of the product (3.2). This is something that
most primitive of EJB CMP engines got correctly in early versions of their products.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira