I'm a worthless newbie but I have managed to get some basic stuff working. I have a giant codebase I'm converting over from our hand-rolled persistent framework to Hibernate based on the examples I have working. I know that proxies can cause some problems with inheritance and downcasting, but I didn't think they caused the ones I'm seeing.
I have a class with a List member containing other objects of that same class; indeed, objects can contain themselves in that list. In a session, I get an object from a query, and then in a recursive member function iterate through the list and do some work, but of course skip recursing on the object itself. However, I'm finding that the proxy object in the list will not compare == to the this pointer and I get infinite recursion. (This code worked fine with our old persistence mechanism, which did not use proxies.) So I decided to compare the objects' hibernateId members, by comparing the private Long members. This also failed because the hibernateId of the objects in the list were null-- even though they just came out of the database. However, if I accessed the ids via an accessor function, which does nothing but return the private member, it returns the right id and the recursion is pruned. Bizarrely, if I print out the member right after a call to the accessor, it is still null. I can alternate between the member and the call over and over and see alternating null, not-null. How can this even be possible? I cannot duplicate this behavior with a smaller file though I keep trying.
All this is happening within one session, where I thought JVM identity was guaranteed, though I'm a little confused about whether a proxy ever is == a real object. Also, accessing a private member in scope should be enough to load/initialize a proxy, right? You don't have to use a method?
Hibernate version: 3.2
Mapping documents:
?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field" default-cascade="save-update">
<class name="meca.model.MecaObjectType" table="MecaObjectType">
<id name="hibernateId" column="MecaObjectType_id">
<generator class="native"/>
</id>
<component name="mecaKey" class="meca.model.MecaKey">
<property name="key" column="mecaKey_key" type="text"/>
</component>
<property name="name" type="text"/>
<property name="description" type="text"/>
<property name="hideNavigationLinks"/>
<list name="questionSections" table="MecaObjectType_questionSections">
<key column="questionSections_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.QuestionSection"/>
</list>
<list name="readOnlyAttributes" table="MecaObjectType_readOnlyAttributes">
<key column="readOnlyAttributes_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.ReadOnlyAttribute"/>
</list>
<list name="formulas" table="MecaObjectType_formulas">
<key column="formulas_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.Formula"/>
</list>
<map name="defaultResponseStringMap" table="MecaObjectType_defaultResponseS\tringMap">
<key column="MecaObjectType_id"/>
<index-many-to-many class="meca.model.Question" column="Question_id"/>
<element column="element" type="text"/>
</map>
<property name="canBeTaskResponder"/>
<list name="indexNameComponents" table="MecaObjectType_indexNameComponents"\>
<key column="indexNameComponents_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.NameComponent"/>
</list>
<set name="keyAttributes" table="MecaObjectType_keyAttributes">
<key column="keyAttributes_key_id"/>
<many-to-many class="meca.model.Attribute"/>
</set>
<map name="readAccessProfilesByAttribute" table="MecaObjectType_readAccessP\rofilesByAttribute">
<key column="MecaObjectType_id"/>
<index-many-to-many class="meca.model.Attribute" column="Attribute_id"/\>
<many-to-many class="meca.model.NamedProfile" column="NamedProfile_id"\/>
</map>
<map name="writeAccessProfilesByAttribute" table="MecaObjectType_writeAcces\sProfilesByAttribute">
<key column="MecaObjectType_id"/>
<index-many-to-many class="meca.model.Attribute" column="Attribute_id"/\>
<many-to-many class="meca.model.NamedProfile" column="NamedProfile_id"\/>
</map>
<many-to-one lazy="no-proxy" name="firstSortAttribute" class="meca.model.At\tribute" column="firstSortAttribute_id"/>
<many-to-one lazy="no-proxy" name="secondSortAttribute" class="meca.model.A\ttribute" column="secondSortAttribute_id"/>
<many-to-one lazy="no-proxy" name="thirdSortAttribute" class="meca.model.At\tribute" column="thirdSortAttribute_id"/>
<list name="sortAttributePrimitives" table="MecaObjectType_sortAttributePri\mitives">
<key column="sortAttributePrimitives_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.Attribute"/>
</list>
<list name="searchAttributes" table="MecaObjectType_searchAttributes">
<key column="searchAttributes_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.Attribute"/>
</list>
<list name="notificationAttributes" table="MecaObjectType_notificationAttri\butes">
<key column="notificationAttributes_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.Attribute"/>
</list>
<many-to-one lazy="no-proxy" name="quickFindDefaultAttribute" class="meca.m\odel.Attribute" column="quickFindDefaultAttribute_id"/>
<property name="hasInstances"/>
<many-to-one lazy="no-proxy" name="defaultReadAccessProfile" class="meca.mo\del.NamedProfile" column="defaultReadAccessProfile_id"/>
<many-to-one lazy="no-proxy" name="defaultWriteAccessProfile" class="meca.m\odel.NamedProfile" column="defaultWriteAccessProfile_id"/>
<many-to-one lazy="no-proxy" name="createAccessProfile" class="meca.model.N\amedProfile" column="createAccessProfile_id"/>
<list name="allAccessProfiles" table="MecaObjectType_allAccessProfiles">
<key column="allAccessProfiles_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.NamedProfile"/>
</list>
<many-to-one lazy="no-proxy" name="ownerAttribute" class="meca.model.Attrib\ute" column="ownerAttribute_id"/>
<component name="domain" class="meca.enum.TypeDomain">
<property name="string" column="domain_string" type="text"/>
<property name="value" column="domain_value"/>
</component>
<many-to-one lazy="no-proxy" name="parentType" class="meca.model.MecaObject\Type" column="parentType_id"/>
<list name="typePedigree" table="MecaObjectType_typePedigree">
<key column="typePedigree_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.MecaObjectType"/>
</list>
<many-to-one lazy="no-proxy" name="noObject" class="meca.model.MecaObject" \column="noObject_id"/>
<property name="isBuiltin"/>
<many-to-one lazy="no-proxy" name="nativeKeyAttribute" class="meca.model.Re\adOnlyAttribute" column="nativeKeyAttribute_id"/>
<many-to-one lazy="no-proxy" name="creatorAttribute" class="meca.model.Read\OnlyAttribute" column="creatorAttribute_id"/>
<many-to-one lazy="no-proxy" name="creationDateAttribute" class="meca.model\.ReadOnlyAttribute" column="creationDateAttribute_id"/>
<many-to-one lazy="no-proxy" name="creationDateAndTimeAttribute" class="mec\a.model.ReadOnlyAttribute" column="creationDateAndTimeAttribute_id"/>
<many-to-one lazy="no-proxy" name="lastModifierAttribute" class="meca.model\.ReadOnlyAttribute" column="lastModifierAttribute_id"/>
<many-to-one lazy="no-proxy" name="lastModificationDateAttribute" class="me\ca.model.ReadOnlyAttribute" column="lastModificationDateAttribute_id"/>
<many-to-one lazy="no-proxy" name="lastModificationDateAndTimeAttribute" cl\ass="meca.model.ReadOnlyAttribute" column="lastModificationDateAndTimeAttribute\_id"/>
<list name="linkAttributes" table="MecaObjectType_linkAttributes">
<key column="linkAttributes_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.ReadOnlyAttribute"/>
</list>
<list name="customFormViews" table="MecaObjectType_customFormViews">
<key column="customFormViews_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.CustomFormView"/>
</list>
<many-to-one lazy="no-proxy" name="standardFormView" class="meca.model.Cust\omFormView" column="standardFormView_id"/>
<many-to-one lazy="no-proxy" name="fullIndex" class="meca.model.MecaObjectI\ndex" column="fullIndex_id"/>
<set name="allIndexes" table="MecaObjectType_allIndexes">
<key column="allIndexes_key_id"/>
<many-to-many class="meca.model.MecaObjectIndex"/>
</set>
<property name="isSecondClass"/>
<list name="objectViews" table="MecaObjectType_objectViews">
<key column="objectViews_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.ObjectView"/>
</list>
<list name="savedAnalysisParameterList" table="MecaObjectType_savedAnalysis\ParameterList">
<key column="savedAnalysisParameterList_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.ResponseAnalysisParameters"/>
</list>
<list name="typesSortedBasedOnThis" table="MecaObjectType_typesSortedBasedO\nThis">
<key column="typesSortedBasedOnThis_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.MecaObjectType"/>
</list>
<map name="defaultResponseDependencyMap" table="MecaObjectType_defaultRespo\nseDependencyMap">
<key column="MecaObjectType_id"/>
<index-many-to-many class="meca.model.Question" column="Question_id"/>
<many-to-many class="meca.model.ObjectDependency" column="ObjectDepend\ency_id"/>
</map>
<set name="keySortSearchDependencies" table="MecaObjectType_keySortSearchDe\pendencies">
<key column="keySortSearchDependencies_key_id"/>
<many-to-many class="meca.model.ObjectDependency"/>
</set>
<many-to-one lazy="no-proxy" name="ownerAttributeDependency" class="meca.mo\del.ObjectDependency" column="ownerAttributeDependency_id"/>
<set name="indexNameDependencies" table="MecaObjectType_indexNameDependenci\es">
<key column="indexNameDependencies_key_id"/>
<many-to-many class="meca.model.ObjectDependency"/>
</set>
<many-to-one lazy="no-proxy" name="parentTypeDependency" class="meca.model.\ObjectDependency" column="parentTypeDependency_id"/>
<set name="typePedigreeDependencies" table="MecaObjectType_typePedigreeDepe\ndencies">
<key column="typePedigreeDependencies_key_id"/>
<many-to-many class="meca.model.ObjectDependency"/>
</set>
<property name="hibernateId"/>
<joined-subclass name="meca.model.AdHocDateObjectType" table="AdHocDateObje\ctType">
<key column="MecaObjectType_id"/>
<component name="specificity" class="meca.enum.DateSpecificity">
<property name="string" column="specificity_string" type="text"/>
<property name="value" column="specificity_value"/>
</component>
</joined-subclass>
<joined-subclass name="meca.model.MecaObjectTupleType" table="MecaObjectTup\leType">
<key column="MecaObjectType_id"/>
<list name="tupleTypes" table="MecaObjectTupleType_tupleTypes">
<key column="tupleTypes_key_id"/>
<list-index column="sequence"/>
<many-to-many class="meca.model.MecaObjectType"/>
</list>
<property name="firstElementIsResponder"/>
<set name="tupleTypesDependencies" table="MecaObjectTupleType_tupleType\sDependencies">
<key column="tupleTypesDependencies_key_id"/>
<many-to-many class="meca.model.ObjectDependency"/>
</set>
</joined-subclass>
<joined-subclass name="meca.model.FakeDateObjectType" table="FakeDateObject\Type">
<key column="MecaObjectType_id"/>
<many-to-one lazy="no-proxy" name="yearAttribute" class="meca.model.Rea\dOnlyAttribute" column="yearAttribute_id"/>
<many-to-one lazy="no-proxy" name="monthAttribute" class="meca.model.Re\adOnlyAttribute" column="monthAttribute_id"/>
<many-to-one lazy="no-proxy" name="weekAttribute" class="meca.model.Rea\dOnlyAttribute" column="weekAttribute_id"/>
<many-to-one lazy="no-proxy" name="dayAttribute" class="meca.model.Read\OnlyAttribute" column="dayAttribute_id"/>
</joined-subclass>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
There's a ton, but here's the relevant section; the first printout prints out exactly what I'd expect in the list. The second printout shows the expected hibernate ids and string forms also. The third shows supertype as having a null hibernateId.
MecaObjectTypeSummary[] supertypeSummaries =
new MecaObjectTypeSummary[typePedigree.size()-1];
System.out.println(":::Type pedigree of " + name + " is " +
typePedigree);
Iterator x = typePedigree.iterator();
index = 0;
while (x.hasNext()) {
MecaObjectType supertype = (MecaObjectType)x.next();
System.out.println(":::Comparing " + this + "/" +
hibernateId + " & " + supertype + "/" +
supertype.getHibernateId());
System.out.println(":::SANITY CHECK: " + this +
hibernateId + " & " + supertype + "/" +
supertype.hibernateId);
if (supertype!=this) {
supertypeSummaries[index++] = supertype.createSummary(user);
}
}
Name and version of the database you are using: postgresql 8.0.3
The generated SQL (show_sql=true):
select typepedigr0_.typePedigree_key_id as typePedi1_1_, typepedigr0\_.elt as elt1_, typepedigr0_.sequence as sequence1_, mecaobject1_.MecaObjectTyp\e_id as MecaObje1_224_0_, mecaobject1_.mecaKey_key as mecaKey2_224_0_, mecaobje\ct1_.name as name224_0_, mecaobject1_.description as descript4_224_0_, mecaobje\ct1_.hideNavigationLinks as hideNavi5_224_0_, mecaobject1_.canBeTaskResponder a\s canBeTas6_224_0_, mecaobject1_.firstSortAttribute_id as firstSor7_224_0_, mec\aobject1_.secondSortAttribute_id as secondSo8_224_0_, mecaobject1_.thirdSortAtt\ribute_id as thirdSor9_224_0_, mecaobject1_.quickFindDefaultAttribute_id as qui\ckFi10_224_0_, mecaobject1_.hasInstances as hasInst11_224_0_, mecaobject1_.defa\ultReadAccessProfile_id as default12_224_0_, mecaobject1_.defaultWriteAccessPro\file_id as default13_224_0_, mecaobject1_.createAccessProfile_id as createA14_2\24_0_, mecaobject1_.ownerAttribute_id as ownerAt15_224_0_, mecaobject1_.domain_\string as domain16_224_0_, mecaobject1_.domain_value as domain17_224_0_, mecaob\ject1_.parentType_id as parentType18_224_0_, mecaobject1_.noObject_id as noObje\ct19_224_0_, mecaobject1_.isBuiltin as isBuiltin224_0_, mecaobject1_.nativeKeyA\ttribute_id as nativeK21_224_0_, mecaobject1_.creatorAttribute_id as creator22_\224_0_, mecaobject1_.creationDateAttribute_id as creatio23_224_0_, mecaobject1_\.creationDateAndTimeAttribute_id as creatio24_224_0_, mecaobject1_.lastModifier\Attribute_id as lastMod25_224_0_, mecaobject1_.lastModificationDateAttribute_id\ as lastMod26_224_0_, mecaobject1_.lastModificationDateAndTimeAttribute_id as l\astMod27_224_0_, mecaobject1_.standardFormView_id as standar28_224_0_, mecaobje\ct1_.fullIndex_id as fullIndex29_224_0_, mecaobject1_.isSecondClass as isSecon3\0_224_0_, mecaobject1_.ownerAttributeDependency_id as ownerAt31_224_0_, mecaobj\ect1_.parentTypeDependency_id as parentT32_224_0_, mecaobject1_.hibernateId as \hiberna33_224_0_, mecaobject1_1_.specificity_string as specific2_248_0_, mecaob\ject1_1_.specificity_value as specific3_248_0_, mecaobject1_2_.firstElementIsRe\sponder as firstEle2_249_0_, mecaobject1_3_.yearAttribute_id as yearAttr2_252_0\_, mecaobject1_3_.monthAttribute_id as monthAtt3_252_0_, mecaobject1_3_.weekAtt\ribute_id as weekAttr4_252_0_, mecaobject1_3_.dayAttribute_id as dayAttri5_252_\0_, case when mecaobject1_1_.MecaObjectType_id is not null then 1 when mecaobje\ct1_2_.MecaObjectType_id is not null then 2 when mecaobject1_3_.MecaObjectType_\id is not null then 3 when mecaobject1_.MecaObjectType_id is not null then 0 en\d as clazz_0_ from MecaObjectType_typePedigree typepedigr0_ left outer join Mec\aObjectType mecaobject1_ on typepedigr0_.elt=mecaobject1_.MecaObjectType_id lef\t outer join AdHocDateObjectType mecaobject1_1_ on mecaobject1_.MecaObjectType_\id=mecaobject1_1_.MecaObjectType_id left outer join MecaObjectTupleType mecaobj\ect1_2_ on mecaobject1_.MecaObjectType_id=mecaobject1_2_.MecaObjectType_id left\ outer join FakeDateObjectType mecaobject1_3_ on mecaobject1_.MecaObjectType_id\=mecaobject1_3_.MecaObjectType_id where typepedigr0_.typePedigree_key_id=?
Debug level Hibernate log excerpt:
|