[hibernate-issues] [Hibernate-JIRA] Created: (HHH-2090) Hibernate violates referential integrity of an underlying database

Andrei Iltchenko (JIRA) noreply at atlassian.com
Tue Sep 19 15:51:24 EDT 2006


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.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira




More information about the hibernate-issues mailing list