-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Hibernate loading incorrect sublcass - please help
PostPosted: Fri Jun 05, 2009 2:22 am 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Hi, I'm getting the following exception, where Hibernate is trying to load a subclass of one type, but the object being returned is another type. The type that Hibernate is expecting is actually wrong. ID 2 really is supposed to be OpenEndedQuestion. So the type it's loading is fine... I dunno why Hibernate is complaining for.

Here is the exception:
Code:
org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException: Object with id: 2 was not of the specified subclass: jobprep.domain.finite.FiniteQuestion (loaded object was of wrong class class jobprep.domain.openended.OpenEndedQuestion); nested exception is org.hibernate.WrongClassException: Object with id: 2 was not of the specified subclass: jobprep.domain.finite.FiniteQuestion (loaded object was of wrong class class jobprep.domain.openended.OpenEndedQuestion)
   at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:663)
   at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
   at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:424)
   at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
   at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:525)
   at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:519)
   at jobprep.dao.ActiveModuleDaoImpl.find(ActiveModuleDaoImpl.java:34)
   at jobprep.service.StudentServiceTests.assertActiveModuleContainsAmountOfActiveQuestions(StudentServiceTests.java:173)
   at jobprep.service.StudentServiceTests.testEntireModuleToCompleteWithUser(StudentServiceTests.java:140)
   at jobprep.service.StudentServiceTests.testEntireModuleToComplete(StudentServiceTests.java:120)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
   at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
   at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
   at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
   at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
   at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
   at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
   at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
   at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
   at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
   at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
   at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
   at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
   at com.intellij.rt.junit4.Junit4TestMethodAdapter.run(Junit4TestMethodAdapter.java:62)
   at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)


Here are two parts of the mapping where this could be the issue:
Code:
    <class name="jobprep.domain.Question" table="question" abstract="true">
        <id name="id" column="question_id" type="long">
            <generator class="sequence">
                <param name="sequence">question_id_sequence</param>
            </generator>
        </id>
       <discriminator column="type" type="string"/>

       <many-to-one name="task" class="jobprep.domain.Task" column="task_id" not-null="true" />
       <property name="text" column="text"/>
       <property name="ordering" column="ordering"/>
       <property name="successMessage" column="success_message"/>

       <bag name="activeQuestions" inverse="true" cascade="all-delete-orphan" lazy="true">
           <key column="question_id" not-null="true"/>
           <one-to-many class="jobprep.domain.ActiveQuestion"/>
       </bag>

       <subclass name="jobprep.domain.finite.FiniteQuestion" discriminator-value="FINITE">
          <bag name="expectedAnswers" inverse="true" cascade="all-delete-orphan" order-by="text asc" lazy="false">
              <key column="question_id" not-null="true"/>
              <one-to-many class="jobprep.domain.finite.QuestionKeyword" />
          </bag>
           <bag name="hints" inverse="true" cascade="all-delete-orphan" order-by="attempts_needed asc" lazy="false">
               <key column="question_id" not-null="true"/>
               <one-to-many class="jobprep.domain.Hint"/>
           </bag>
          <property name="engineType" column="engine_type"/>
          <property name="minimumNumberOfKeywords" column="minimum_number_of_keywords"/>
       </subclass>

       <subclass name="jobprep.domain.openended.OpenEndedQuestion" discriminator-value="OPEN-ENDED">
          <bag name="keywords" inverse="true" cascade="all-delete-orphan" order-by="text asc" lazy="false">
              <key column="question_id" not-null="true"/>
              <one-to-many class="jobprep.domain.finite.QuestionKeyword" />
          </bag>
          <property name="minimumNumberOfWords" column="minimum_number_of_words"/>
          <property name="minimumNumberOfWordsForFirstAttempt" column="minimum_number_of_words_for_first_attempt"/>
          <property name="minimumNumberOfSentences" column="minimum_number_of_sentences"/>
          <property name="minimumNumberOfKeywords" column="minimum_number_of_keywords"/>
       </subclass>

       <subclass name="jobprep.domain.category.CategoryBasedQuestion" discriminator-value="CATEGORY-BASED">
         <bag name="categories" inverse="true" cascade="all-delete-orphan" order-by="name asc" lazy="false">
              <key column="question_id" not-null="true"/>
              <one-to-many class="jobprep.domain.category.Category"/>
          </bag>
       </subclass>
    </class>


and this one:

Code:
   <class name="jobprep.domain.ActiveQuestion" table="active_question">
       <id name="id" column="active_question_id" type="long">
           <generator class="sequence">
               <param name="sequence">active_question_id_sequence</param>
           </generator>
       </id>
      <many-to-one name="activeTask" class="jobprep.domain.ActiveTask" column="active_task_id"
                   not-null="true" cascade="all" />
      <component name="results" class="jobprep.domain.QuestionResults">
         <bag name="results" inverse="true" cascade="all-delete-orphan" lazy="false"
            order-by="creation_date asc">
             <key column="active_question_id" not-null="true"/>
             <one-to-many class="jobprep.domain.QuestionResult"/>
         </bag>
      </component>
      <property name="isAnswered" column="is_answered"/>
       <property name="failedAttempts" column="failed_attempts"/>
       <many-to-one name="question" class="jobprep.domain.Question" column="question_id"
                    not-null="true" cascade="none" />
      <property name="creationDate" column="creation_date"/>
   </class>

   <class name="jobprep.domain.QuestionResult" table="question_result" abstract="true">
       <id name="id" column="question_result_id" type="long">
           <generator class="sequence">
               <param name="sequence">question_result_id_sequence</param>
           </generator>
       </id>
      <discriminator column="type" type="string"/>

      <many-to-one name="activeQuestion" class="jobprep.domain.ActiveQuestion" column="active_question_id"
                   not-null="true" cascade="none" />
      <property name="creationDate" column="creation_date"/>

      <subclass name="jobprep.domain.finite.FiniteResult" discriminator-value="FINITE">
         <many-to-one name="question" class="jobprep.domain.finite.FiniteQuestion" column="question_id"
                      not-null="true" cascade="all" />
         <property name="suppliedAnswer" column="supplied_answer"/>
         <bag name="answers" table="question_result_to_answer" inverse="true"
              cascade="all" order-by="text asc" lazy="false">
             <key column="question_result_id" not-null="true"/>
             <many-to-many class="jobprep.domain.finite.QuestionKeyword" column="answer_id" />
         </bag>
      </subclass>

      <subclass name="jobprep.domain.openended.OpenEndedResult" discriminator-value="OPEN-ENDED">
         <many-to-one name="question" class="jobprep.domain.openended.OpenEndedQuestion" column="question_id"
                      not-null="true" cascade="all" />
         <property name="suppliedAnswer" column="supplied_answer"/>
      </subclass>

      <subclass name="jobprep.domain.category.CategoryBasedResult" discriminator-value="CATEGORY-BASED">
         <many-to-one name="question" class="jobprep.domain.category.CategoryBasedQuestion" column="question_id"
                      not-null="true" cascade="all" />
         <bag name="answers" table="question_result_to_category_answer" inverse="true"
              cascade="all" order-by="text asc" lazy="false">
             <key column="question_result_id" not-null="true"/>
             <many-to-many class="jobprep.domain.category.CategoryAnswer" column="category_answer_id" />
         </bag>
      </subclass>
   </class>


Lastly, here are the database rows:

Code:
   <question question_id="1" text="What is Sisko's first name?" task_id="1"
             ordering="0" success_message="Correct!" type="FINITE" engine_type="SINGLE"
             minimum_number_of_keywords="1" />
   <question question_id="2" text="Why do you like Star Trek?" task_id="1"
             ordering="1" success_message="You are a geek!" type="OPEN-ENDED"
             minimum_number_of_keywords="0" minimum_number_of_words="4"
             minimum_number_of_sentences="1" minimum_number_of_words_for_first_attempt="2"/>
   <question question_id="3" text="Match the Star Trek items" task_id="1"
             ordering="2" success_message="You are a trekkie!" type="CATEGORY-BASED"/>
   <question question_id="4" text="Am I done yet?" task_id="2"
             ordering="0" success_message="" type="OPEN-ENDED"
             minimum_number_of_keywords="0" minimum_number_of_words="4"
             minimum_number_of_sentences="1" minimum_number_of_words_for_first_attempt="2"/>


Clearly, id 2 is of type "OPEN-ENDED", so it doesn't make much sense why hibernate wants "FINITE" :( Hibernate is messed up.

This error happens when loading a totally different object... but when it's loading the object graph, something went wrong I guess? I dunno. I don't understand why this is failing.

Help?


Top
 Profile  
 
 Post subject: Re: Hibernate loading incorrect sublcass - please help
PostPosted: Fri Jun 05, 2009 3:38 am 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Another interesting comment is that if I wrap all of this up with @Transactional in spring, I don't get this exception. Does that help diagnose what the problem is? The thing is, the service test case really needs to be @NotTransactional, because I'm testing several service methods in a single test case, kind of simulating the flow of many service calls the way a user would call them. The same error happens if I actually navigate the actual web tier in the app server too... so that's why I write integration tests like this to make sure it works.


Last edited by mystic on Fri Jun 05, 2009 6:21 am, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: Hibernate loading incorrect sublcass - please help
PostPosted: Fri Jun 05, 2009 4:36 am 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Okay... after screwing around with this for several hours, I still can't find the solution. The problem happens when valid data is put into the QuestionResult objects, that also points to valid ActiveQuestion and specific Question objects. If I delete this data out of the database, Hibernate shuts up and is fine. Basically any record that gets read by hibernate will cause it to go haywire...

:(

This makes no sense. The data is fine. The hbm file is fine. Am I doing something Hibernate cannot simply do? Is referencing subclasses in my FiniteResult, OpenEndedResult and CategoryResult mappings the real issue here? That they need to be loaded twice?


Top
 Profile  
 
 Post subject: Re: Hibernate loading incorrect sublcass - please help
PostPosted: Fri Jun 05, 2009 6:19 am 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Okay... this is officially the stupidest solution ever...

in QuestionResult, I changed the mapping to have:

Code:
   <class name="jobprep.domain.QuestionResult" table="question_result" abstract="true">
...
      <many-to-one name="question" class="jobprep.domain.Question"
                   column="question_id" not-null="true" cascade="none" />


I took away the individual references to the question subclasses in the subclassed results. I just create a getter in the subclasses that casts it manually to the object that is to be there. Sure enough, it works.

This leads me to believe that hibernate has a bug here. My mapping was fine above, and since I'm casting it myself and it works... hibernate's type checking with this example clearly does not work as it should.

This is really a bug in hibernate :(


Top
 Profile  
 
 Post subject: Re: Hibernate loading incorrect sublcass - please help
PostPosted: Fri Jun 05, 2009 8:29 am 
Hibernate Team
Hibernate Team

Joined: Thu Dec 18, 2003 9:55 am
Posts: 1977
Location: France
Hi,

Could you simplify the issue with 3 entities (2 level Inheritance hierarchy) + associated entity + 1 test class.
The problem with your post is that it is not easy to read and as we don't have a lot of time, it's difficult to reproduce it.

_________________
Anthony,
Get value thanks to your skills: http://www.redhat.com/certification


Top
 Profile  
 
 Post subject: Re: Hibernate loading incorrect sublcass - please help
PostPosted: Fri Jun 05, 2009 3:28 pm 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
I don't see how to simplify the object model.

I have Question as base class. There are 3 types of questions, and all containing similar functionality, and also very different functionality and properties.

When an answer is supplied to Question.submitAnswer(), it returns a QuestionResult, which contains a lot of information about the question above and beyond the fact that it was answered correctly or not. That way I can use results to give good error messages, as well as track all the answers the user tried until they got the question right.

Since the results vary based on type, it makes sense to have a result per question subclass. I really do think the correct design is to have a result know about the specific question subclass. When coding these classes as POJOs, that is what makes the most sense. That's why I put a FiniteQuestion into a FiniteResult, and so on for all the question types.

I really do think this is a bug that I am forced to only map type Question to a QuestionResult, and then I have to manually cast to the appropriate Question type in my POJO getter code in the subclasses. Hibernate's type checking/casting is not working right when it has a many-to-one reference to another subclass while also inside the <subclass> element... because the type being generated with id=2 is correct, but Hibernate's validation on that type is not correct.

Basically, Hibernate only validates FiniteQuestion types... but once it pulls out a CategoryQuestion or OpenEndedQuestion - BAM - it fails expecting a FiniteQuestion.

This bug is so bad that every query will fail (even get() or load()) until the rows inside question_result are deleted. This is very, VERY bad. The queries and data are perfectly fine.

I think there is a bug in the <Subclass> code for handling relationships to other polymorphic relationships... Hibernate only expects the "first" type declared (that's how it appears... in my case, it happens to be "FiniteQuestion").

I think this is pretty serious as the only change I did was remove the Question references from QuestionResult subclasses... and I put it in the QuestionResult base class instead. Since I'm casting manually and it works, I can only assume the problem is with Hibernate.

I hope you find this info helpful to fixing it for future versions.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.