-->
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.  [ 32 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Inheritance and Interface proxies cause ClassCastException
PostPosted: Sat Aug 21, 2004 11:26 am 
Newbie

Joined: Thu May 06, 2004 4:25 pm
Posts: 13
Location: Prague, Czech Republic
I've got a problem with using interface proxies together with inheritance (joined-subclasses). This problem is described in this thread
http://forum.hibernate.org/viewtopic.php?t=927010. Simply, it causes that a CGLib proxy has a RANDOM interface.

I'd like to ask, if anything has changed. Or if you still thing this is a correct behaviour. (Gavin:
Quote:
This is all correct behavior for interface proxies. Try using concrete class proxies instead.
). I do not agreee that this is correct and a feel it should be reworked. Is it possible this behavour will change in future versions?

One of the reasons I've chosen Hibernate is its support for inheritance mappings. But with this issue I would have to give up inheritance or proxies. (concrete class proxies are not suitable for inheritance mappings).

I use a following workaround, but I don't think this is correct :)
net.sf.hibernate.proxy.CGLIBLazyInitializer.intercept() method looks as follows:
Code:
public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
    if (constructed) {
        Object result = invoke(method, args, obj);
        if (result == INVOKE_IMPLEMENTATION) {
            try {
                return proxy.invoke(getImplementation(), args);
            } catch (ClassCastException e) {
             Log logger = LogFactory.getLog(LazyInitializer.class);
             if(logger.isWarnEnabled()) {
                 logger.warn("CGLIB proxy error - ClassCastException for method "+method.getName()+" on proxy "+proxy+". Using reflection workaround.");
             }

             Object o = getImplementation();
                //find the real method to be invoking on instead of the wrong interface one
                Method implMethod = o.getClass().getMethod(method.getName(), method.getParameterTypes());
                return implMethod.invoke(o, args);
            }
        } else {
            return result;
        }
    } else {
        //while constructor is running
        return proxy.invokeSuper(obj, args);
    }
}



Simply using reflection if ClassCastException is thrown.

Thank you very much for an answer. I'd like to throw away this terrible hack.

Regards
Pavel


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 21, 2004 11:37 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
If you write your interfaces correctly, there is no problem here.

You have not demonstrated any problem at all.

B extends A
AImpl implements A
BImpl extends AImpl implements B

works just beautifully in Hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 21, 2004 11:58 am 
Newbie

Joined: Thu May 06, 2004 4:25 pm
Posts: 13
Location: Prague, Czech Republic
I've got a same problem as discussed in http://forum.hibernate.org/viewtopic.php?t=927010

class AImpl implements A
interface B extends A
class BImpl implements B
interface C extends A
class CImpl implements C
interface D extends C
class DImpl implements D

Classes are mapped using joined-subclass and proxies. But the CGlib proxy uses a random interface. When I for example select a D class and try to call its method, ClassCastException is thrown, bacause proxy has a random interface for example B. See the older discussion for more details (you replied that this is correct)

Thank you very much
Pavel


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 21, 2004 12:05 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
And does BImpl extend AImpl, etc??

I'm sorry, I don't see any actual problem described in the other threads, just handwaving, which I ignore.

Trust me, there is certainly no random behavior here, so if you can describe your problem without handwaving, perhaps I can help.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 21, 2004 12:12 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
By the way, problem descriptions always involve code.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 21, 2004 12:50 pm 
Newbie

Joined: Thu May 06, 2004 4:25 pm
Posts: 13
Location: Prague, Czech Republic
OK. Sorry for handwaving. I'm not an english native speaker, so I don't have a right feeling for "handwaving" :)

Here is my mapping:
Code:
<hibernate-mapping package="cz.presto.didacticus.study">
   <class name="BlockImpl" table="T_BLOCK" proxy="Block">
      <id name="id" type="integer" column="ID" >
         <generator class="sequence">
            <param name="sequence">G_BLOCK_ID</param>
         </generator>
      </id>

      <property name="shortName" column="NAME_SHORT" length="20" not-null="true" type="string" />
      <property name="nameLid" column="NAME_LID" type="integer" not-null="true" length="10" />
      <property name="descriptionLid" column="DESCRIPTION_LID" type="integer" not-null="true" length="10" />
      <property name="revisionId" column="REVISION_ID" type="integer" not-null="false" length="10" />
      <property name="structureType" column="STRUCTURE_TYPE" type="integer" not-null="false" length="5" />
      <property name="timeLimit" column="TIME_LIMIT" type="timestamp"  not-null="false" length="8" />
      <property name="timeLimitOpt" column="TIME_LIMIT_OPT" type="integer"  not-null="false" length="5" />
      <property name="kindId" column="KIND_ID" type="integer" not-null="false" length="5" />
      <property name="typeId" column="TYPE_ID" type="int" not-null="true" insert="false" update="false"/>

      <joined-subclass name="Block14Impl" table="T_B14" proxy="Block14">
         <key column="BLOCK_ID"/>
         <set name="block14Items" cascade="all" inverse="true" lazy="true" order-by="LIST_ORDER">
            <key column="BLOCK_ID"/>
            <one-to-many class="Block14ItemImpl"/>
         </set>
      </joined-subclass>
   
      <joined-subclass name="Block15Impl" table="T_B15" proxy="Block15">
         <key column="BLOCK_ID"/>
         <set name="block15Items" cascade="all" inverse="true" lazy="true">
            <key column="BLOCK_ID"/>
            <one-to-many class="Block15ItemImpl"/>
         </set>
      </joined-subclass>

      <joined-subclass name="Block11Impl" table="T_B11" proxy="Block11">
         <key column="BLOCK_ID"/>
         <property name="resourceLid" column="RESOURCE_LID" not-null="true" type="integer"/>
      </joined-subclass>
      
      

      <joined-subclass name="ExerciseImpl" table="T_EXERCISE" proxy="Exercise">
         <key column="BLOCK_ID"/>
         <bag name="problems" table="T_EXERCISE_X_PROBLEM" cascade="all" inverse="true" lazy="true">
            <key column="EXERCISE_ID" />
            <many-to-many column="PROBLEM_ID" class="ProblemImpl"/>
         </bag>
         <property name="taskLid" column="TASK_LID" type="integer"/>
         <property name="example" column="EXAMPLE" type="string">
            
         </property>
         
         <joined-subclass name="Exercise18Impl" table="T_E18" proxy="Exercise18">
            <key column="EXERCISE_ID"/>
            <property name="direction" column="DIRECTION_ON" type="integer" />
         </joined-subclass>
         
         <joined-subclass name="Exercise20Impl" table="T_E20" proxy="Exercise20">
            <key column="EXERCISE_ID"/>
            <property name="justOne" column="JUST_ONE" type="integer" />
         </joined-subclass>         
         
         <joined-subclass name="Exercise21Impl" table="T_E21" proxy="Exercise21">
            <key column="EXERCISE_ID"/>
         </joined-subclass>         
      </joined-subclass>


   </class>
   
</hibernate-mapping>


The Java classes are generated by HibernateSynchronizer in this way:

interface Block extends Serializable
class BaseBlockImpl implements Block
class BlockImpl extends BaseBlockImpl

interface Block14 extends Block
class BaseBlock14 extends BlockImpl implements Block14
class Block14Impl extends BaseBlock14Impl

interface Exercise extends Block
class BaseExerciseImpl extends BlockImpl implements Exercise
class ExerciseImpl extends BaseExerciseImpl

interface Exercise18 extends Exercise
class BaseExercise18Impl extends ExerciseImpl implements Exercise18
class Exercise18Impl extends BaseExerciseImpl

...the others are the same.

The block is then selected as follows, but also in many other ways (query returning list, etc)

Code:
block = (Block) session.load(Exercise18Impl.class, blockId);


and then I call some method on the block, for example
Code:
block.getId()


I should mention that session is closed. The exception is:

Code:
java.lang.ClassCastException
   at cz.presto.didacticus.study.Exercise21$$FastClassByCGLIB$$bafef9b3.invoke(<generated>)
   at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
   at net.sf.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:108)
   at cz.presto.didacticus.study.Exercise21$$EnhancerByCGLIB$$cae49957.getId(<generated>)
   at cz.presto.didacticus.study.logic.StudyBean.getUnitContentItem(StudyBean.java:109)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:324)
   at org.springframework.aop.framework.AopProxyUtils.invokeJoinpointUsingReflection(AopProxyUtils.java:59)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:149)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:118)
   at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:191)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:138)
   at org.springframework.aop.interceptor.PerformanceMonitorInterceptor.invoke(PerformanceMonitorInterceptor.java:46)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:138)
   at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:148)
   at $Proxy1.getUnitContentItem(Unknown Source)
   at cz.presto.didacticus.study.web.BlockDispatcher.handleRequestInternal(BlockDispatcher.java:65)
   at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:121)
   at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:45)
   at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:485)
   at org.springframework.web.servlet.FrameworkServlet.serviceWrapper(FrameworkServlet.java:342)
   at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:318)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:743)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:856)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:284)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:204)
   at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:73)
   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:73)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:233)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:204)
   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:257)
   at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:151)
   at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:567)
   at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:245)
   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:199)
   at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:151)
   at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:567)
   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:184)
   at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:151)
   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:164)
   at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:149)
   at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:567)
   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:156)
   at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:151)
   at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:567)
   at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:972)
   at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:206)
   at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:833)
   at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:732)
   at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:619)
   at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:688)
   at java.lang.Thread.run(Thread.java:534)


This exception was thrown after the selected Block was of type Block14 (dicovered in debugger - toString() prints Block14Impl, but in the exception you can see Exercise21).

As I read over my reply, I feel like I should be a newbie. I tried to solve it myself in many ways following docs and this forum, but it failed.

Thaks
Pavel


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 21, 2004 8:46 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
So, I added this mapping to the test suite:

Code:
<?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
   package="org.hibernate.test.interfaceproxy">
   
   <class name="ItemImpl" proxy="Item">
      <id name="id">
         <generator class="native"/>
      </id>
      <property name="name"/>
      <joined-subclass name="DocumentImpl"
         proxy="Document">
         <key column="id"/>
         <property name="content"/>
         <property name="modified"/>
         <property name="created"/>
         <joined-subclass name="SecureDocumentImpl"
            proxy="SecureDocument">
            <key column="documentId"/>
            <property name="permissionBits"/>
            <property name="owner"/>
      </joined-subclass>
      </joined-subclass>
      <joined-subclass name="FolderImpl"
         proxy="Folder">
         <key column="id"/>
         <many-to-one name="parent"
            class="FolderImpl"/>
      </joined-subclass>
   </class>
   
</hibernate-mapping>


Then this test passes:

Code:
   public void testInterfaceProxies() {
      Session s = openSession();
      Transaction t = s.beginTransaction();
      Document d = new DocumentImpl();
      d.setName("Hibernate in Action");
      d.setContent( Hibernate.createBlob( "blah blah blah".getBytes() ) );
      Long did = (Long) s.save(d);
      SecureDocument d2 = new SecureDocumentImpl();
      d2.setName("Secret");
      d2.setContent( Hibernate.createBlob( "wxyz wxyz".getBytes() ) );
      d2.setPermissionBits( (byte) 664 );
      d2.setOwner("gavin");
      Long d2id = (Long) s.save(d2);
      t.commit();
      s.close();

      s = openSession();
      t = s.beginTransaction();
      d = (Document) s.load(ItemImpl.class, did);
      assertEquals( did, d.getId() );
      assertEquals( "Hibernate in Action", d.getName() );
      assertNotNull( d.getContent() );
      
      d2 = (SecureDocument) s.load(ItemImpl.class, d2id);
      assertEquals( d2id, d2.getId() );
      assertEquals( "Secret", d2.getName() );
      assertNotNull( d2.getContent() );
      
      s.clear();
      
      d = (Document) s.load(DocumentImpl.class, did);
      assertEquals( did, d.getId() );
      assertEquals( "Hibernate in Action", d.getName() );
      assertNotNull( d.getContent() );
      
      d2 = (SecureDocument) s.load(SecureDocumentImpl.class, d2id);
      assertEquals( d2id, d2.getId() );
      assertEquals( "Secret", d2.getName() );
      assertNotNull( d2.getContent() );
      assertEquals( "gavin", d2.getOwner() );
      
      //s.clear();
      
      d2 = (SecureDocument) s.load(SecureDocumentImpl.class, did);
      assertEquals( did, d2.getId() );
      assertEquals( "Hibernate in Action", d2.getName() );
      assertNotNull( d2.getContent() );

      try {
         d2.getOwner(); //CCE
         assertFalse(true);
      }
      catch (ClassCastException cce) {
         //correct
      }
      
      
      t.commit();
      s.close();
   }


As you can see, the only way I was able to induce a CCE is if I tried to use a document as a securedocument. Arguably, Hibernate should fail earlier in this case, but that is a little difficult to implement, since the proxy does not resolve its reference until it is invoked.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 21, 2004 8:50 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
If you still believe there is some problem here, you need to submit an as-simple-as-possible, runnable test case to JIRA.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 28, 2004 12:33 pm 
Newbie

Joined: Thu May 06, 2004 4:25 pm
Posts: 13
Location: Prague, Czech Republic
Hi Gavin,
I've prepared as simple as possible testcase producing my "interface mismatch". I think it is possible to refactor your testcase to be similar to mine.

Mapping:
Code:
<hibernate-mapping package="cz.presto.didacticus.study">
   <class name="BlockImpl" table="T_BLOCK" proxy="Block">
      <id name="id" type="integer" column="ID" >
         <generator class="sequence">
            <param name="sequence">G_BLOCK_ID</param>
         </generator>
      </id>

      <set name="blockItems" cascade="all" inverse="true" lazy="true" order-by="LIST_ORDER">
         <key column="BLOCK_ID"/>
         <one-to-many class="Block14ItemImpl"/>
      </set>

      <joined-subclass name="Block14Impl" table="T_B14" proxy="Block14">
         <key column="BLOCK_ID"/>
      </joined-subclass>
      <joined-subclass name="Block15Impl" table="T_B15" proxy="Block15">
         <key column="BLOCK_ID"/>
      </joined-subclass>
      <joined-subclass name="Block11Impl" table="T_B11" proxy="Block11">
         <key column="BLOCK_ID"/>
      </joined-subclass>

      <joined-subclass name="ExerciseImpl" table="T_EXERCISE" proxy="Exercise">
         <key column="BLOCK_ID"/>
         <joined-subclass name="Exercise18Impl" table="T_E18" proxy="Exercise18">
            <key column="EXERCISE_ID"/>
         </joined-subclass>
         <joined-subclass name="Exercise20Impl" table="T_E20" proxy="Exercise20">
            <key column="EXERCISE_ID"/>
         </joined-subclass>         
         <joined-subclass name="Exercise21Impl" table="T_E21" proxy="Exercise21">
            <key column="EXERCISE_ID"/>
         </joined-subclass>         
      </joined-subclass>
   </class>

   <class name="Block14ItemImpl" table="T_B14_ITEM" proxy="Block14Item">
      <composite-id class="Block14ItemPK" name="id">
         <key-property name="listOrder" column="LIST_ORDER" type="int" />
         <key-many-to-one name="block" class="BlockImpl" column="BLOCK_ID" />
      </composite-id>
   </class>

    <query name="Block14.loadInitialize">
       <![CDATA[
          select block
          from Block14Impl as block
             left join fetch block.blockItems as item
          where block.id=?
       ]]>
    </query>

</hibernate-mapping>


Testcase:
Code:
    public void testClassMismatch() throws Exception {
      Block block = null;
        SessionFactory factory = TestUtils.getSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();

      Query queryObject = session.getNamedQuery("Block14.loadInitialize");
      queryObject.setParameter(0, new Integer(1));
      block = (Block) queryObject.uniqueResult();
        System.out.println("Block14: "+block+" class: "+block.getClass().getName());
        assertTrue(block instanceof Block14);
        assertTrue(Hibernate.isInitialized(block.getBlockItems()));
        Block14 block14 = (Block14) block;
        assertEquals(new Integer(1), block14.getId());
       
        transaction.commit();
        session.close();
    }


and the result. Note the System.out producing the difference between toString() and getClass().getName()
Code:
Block14: cz.presto.didacticus.study.Block14Impl@fe5757cb class: cz.presto.didacticus.study.Exercise20$$EnhancerByCGLIB$$c03c061f
E
Time: 9,73
There was 1 error:
1) testClassMismatch(cz.presto.didacticus.test.ProxyClassMismatchTest)java.lang.ClassCastException
   at cz.presto.didacticus.study.Exercise20$$FastClassByCGLIB$$bafef9b2.invoke(<generated>)
   at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
   at net.sf.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:108)
   at cz.presto.didacticus.study.Exercise20$$EnhancerByCGLIB$$c03c061f.getId(<generated>)
   at cz.presto.didacticus.test.ProxyClassMismatchTest.testClassMismatch(ProxyClassMismatchTest.java:45)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at cz.presto.didacticus.test.ProxyClassMismatchTest.main(ProxyClassMismatchTest.java:52)


Thank you for your time
Pavel


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 28, 2004 12:59 pm 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
It can be related to reflection bugs in some JVM's, I am not sure it is the same problem, but it was reported for cglib a few times, workaround with method lookup in interceptor helps, but it is "slow" and we do not have a good solution at this time.
It can be bug in cglib too if you can reproduce it in different JVM implementations. Try some popular JVM like 1.4.2 .


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 28, 2004 7:44 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
By the way, this is still not "simple", you have about 10 unnecessary subclasses.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 29, 2004 5:06 am 
Newbie

Joined: Thu May 06, 2004 4:25 pm
Posts: 13
Location: Prague, Czech Republic
I've got all these subclasses in my testcase because of the "random" behaviour.

I am using 1.4.2_04 JDK.

I tried more simple test and it produces more "strange behaviour" (I don't like this phrase. I feel like an absolute beginner then :).

Code:
<hibernate-mapping package="cz.presto.didacticus.study">
   <class name="BlockImpl" table="T_BLOCK" proxy="Block">
      <id name="id" type="integer" column="ID" >
         <generator class="sequence">
            <param name="sequence">G_BLOCK_ID</param>
         </generator>
      </id>

      <set name="blockItems" cascade="all" inverse="true" lazy="true" order-by="LIST_ORDER">
         <key column="BLOCK_ID"/>
         <one-to-many class="Block14ItemImpl"/>
      </set>

      <joined-subclass name="Block14Impl" table="T_B14" proxy="Block14">
         <key column="BLOCK_ID"/>
      </joined-subclass>

      <joined-subclass name="ExerciseImpl" table="T_EXERCISE" proxy="Exercise">
         <key column="BLOCK_ID"/>
         <joined-subclass name="Exercise20Impl" table="T_E20" proxy="Exercise20">
            <key column="EXERCISE_ID"/>
         </joined-subclass>         
      </joined-subclass>
   </class>

   <class name="Block14ItemImpl" table="T_B14_ITEM" proxy="Block14Item">
      <composite-id class="Block14ItemPK" name="id">
         <key-property name="listOrder" column="LIST_ORDER" type="int" />
         <key-many-to-one name="block" class="BlockImpl" column="BLOCK_ID" />
      </composite-id>
   </class>

    <query name="Block14.loadInitialize">
       <![CDATA[
          select block
          from Block14Impl as block
             left join fetch block.blockItems as item
          where block.id=?
       ]]>
    </query>

</hibernate-mapping>


The testcase is the same and the result is:
Code:
Block14: cz.presto.didacticus.study.Block14Impl@fe5757cb class: cz.presto.didacticus.study.Block14$$EnhancerByCGLIB$$54d5e211
E
Time: 9
There was 1 error:
1) testClassMismatch(cz.presto.didacticus.test.ProxyClassMismatchTest)2004-08-29 10:54:14,109 WARN net.sf.hibernate.impl.SessionImpl - afterTransactionCompletion() was never called
2004-08-29 10:54:14,109 WARN net.sf.hibernate.impl.SessionImpl - unclosed connection
net.sf.hibernate.HibernateException: identifier of an instance of cz.presto.didacticus.study.Block14ItemImpl altered from cz.presto.didacticus.study.Block14ItemPK@5898d2d0 to cz.presto.didacticus.study.Block14ItemPK@5898d2d0
   at net.sf.hibernate.impl.SessionImpl.checkId(SessionImpl.java:2642)
   at net.sf.hibernate.impl.SessionImpl.flushEntity(SessionImpl.java:2465)
   at net.sf.hibernate.impl.SessionImpl.flushEntities(SessionImpl.java:2458)
   at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2260)
   at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2239)
   at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
   at cz.presto.didacticus.test.ProxyClassMismatchTest.testClassMismatch(ProxyClassMismatchTest.java:47)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at cz.presto.didacticus.test.ProxyClassMismatchTest.main(ProxyClassMismatchTest.java:52)


Still trying to solve it myself. It would be nice if you could reproduce my exceptions and suggest where could be the problems.

Thanks
P.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 29, 2004 5:11 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I can't reproduce things unless there is a runnable test case in JIRA. However, looks to me more and more like this is a bug in your code, very likely a problem in your implementation of equals/hashCode for the composite id class.

Note that it is quite difficult to implement equals/hashCode correctly, for a <key-many-to-one> to a proxyable class.

I suggest you try eliminating the <key-many-to-one/>


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 29, 2004 5:14 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
By the way, that is what I mean by "simplify". Eliminate all "funny" things, until you reach the one thing that is the source of the problem. You shouldn't post here until you have already done that.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 30, 2004 6:36 am 
Newbie

Joined: Thu May 06, 2004 4:25 pm
Posts: 13
Location: Prague, Czech Republic
I tried to simplify my testcase as much as possible. It wasn't that easy, bacause if I removed something (mapping, subclass, etc.), tescase ran OK. But I tried many combinations throwing my CCE and choose the one I think it is very similar to Gavin's testcase.
It seems to me more and more like an enviroment problem. I'm using Firebird 1.5 with its JCA/JDBC Driver and JRE 1.4.2_04. I tried to use JRE1.3.11, but the driver requires 1.4. Should I try my testcase on a different RDBMS or do you think this is database independent?
I'm becoming more and more despairing as I spent 3 days on this thing. I would really appreciate some hint, my fantasy is over :)

Here is the testcase
Code:
    public void testClassMismatch() throws Exception {
        SessionFactory factory = TestUtils.getSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();

        Block block = (Block) session.load(BlockImpl.class, new Integer(1));
        System.out.println("Block14: "+block+" class: "+block.getClass().getName());
        assertTrue(block instanceof Block14);
        assertEquals(new Integer(1), block.getId()); //this line throws CCE
       
        transaction.commit();
        session.close();
    }


Mapping:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
   
<hibernate-mapping package="cz.presto.didacticus.study">
   <class name="BlockImpl" table="T_BLOCK" proxy="Block">
      <id name="id" type="integer" column="ID" >
         <generator class="sequence">
            <param name="sequence">G_BLOCK_ID</param>
         </generator>
      </id>
      <joined-subclass name="Block14Impl" table="T_B14" proxy="Block14">
         <key column="BLOCK_ID"/>
      </joined-subclass>
      <joined-subclass name="ExerciseImpl" table="T_EXERCISE" proxy="Exercise">
         <key column="BLOCK_ID"/>
         <joined-subclass name="Exercise18Impl" table="T_E18" proxy="Exercise18">
            <key column="EXERCISE_ID"/>
         </joined-subclass>
      </joined-subclass>
   </class>
</hibernate-mapping>


..and the result
Code:
.Hibernate: select blockimpl0_.ID as ID0_, case when blockimpl0__3_.EXERCISE_ID is not null then 1 when blockimpl0__1_.BLOCK_ID is not null then 2 when blockimpl0__2_.BLOCK_ID is not null then 3 when blockimpl0_.ID is not null then 0 end as clazz_0_ from T_BLOCK blockimpl0_ left outer join T_B14 blockimpl0__1_ on blockimpl0_.ID=blockimpl0__1_.BLOCK_ID left outer join T_EXERCISE blockimpl0__2_ on blockimpl0_.ID=blockimpl0__2_.BLOCK_ID left outer join T_E18 blockimpl0__3_ on blockimpl0_.ID=blockimpl0__3_.EXERCISE_ID where blockimpl0_.ID=?
Block14: cz.presto.didacticus.study.Block14Impl@fe5757cb class: cz.presto.didacticus.study.Exercise$$EnhancerByCGLIB$$cc084872
E
Time: 9,78
There was 1 error:
1) testClassMismatch(cz.presto.didacticus.test.ProxyClassMismatchTest)java.lang.ClassCastException
   at cz.presto.didacticus.study.Exercise$$FastClassByCGLIB$$d94d4074.invoke(<generated>)
   at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
   at net.sf.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:108)
   at cz.presto.didacticus.study.Exercise$$EnhancerByCGLIB$$cc084872.getId(<generated>)
   at cz.presto.didacticus.test.ProxyClassMismatchTest.testClassMismatch(ProxyClassMismatchTest.java:36)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at cz.presto.didacticus.test.ProxyClassMismatchTest.main(ProxyClassMismatchTest.java:43)

FAILURES!!!
Tests run: 1,  Failures: 0,  Errors: 1


One more interesting thing demonstrating my confusion. If I switch the mapping of Block14 and Exercise, like following:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
   
<hibernate-mapping package="cz.presto.didacticus.study">
   <class name="BlockImpl" table="T_BLOCK" proxy="Block">
      <id name="id" type="integer" column="ID" >
         <generator class="sequence">
            <param name="sequence">G_BLOCK_ID</param>
         </generator>
      </id>
      <joined-subclass name="ExerciseImpl" table="T_EXERCISE" proxy="Exercise">
         <key column="BLOCK_ID"/>
         <joined-subclass name="Exercise18Impl" table="T_E18" proxy="Exercise18">
            <key column="EXERCISE_ID"/>
         </joined-subclass>
      </joined-subclass>
      <joined-subclass name="Block14Impl" table="T_B14" proxy="Block14">
         <key column="BLOCK_ID"/>
      </joined-subclass>
   </class>
</hibernate-mapping>


..everything looks fine:
Code:
.Hibernate: select blockimpl0_.ID as ID0_, case when blockimpl0__2_.EXERCISE_ID is not null then 1 when blockimpl0__1_.BLOCK_ID is not null then 2 when blockimpl0__3_.BLOCK_ID is not null then 3 when blockimpl0_.ID is not null then 0 end as clazz_0_ from T_BLOCK blockimpl0_ left outer join T_EXERCISE blockimpl0__1_ on blockimpl0_.ID=blockimpl0__1_.BLOCK_ID left outer join T_E18 blockimpl0__2_ on blockimpl0_.ID=blockimpl0__2_.EXERCISE_ID left outer join T_B14 blockimpl0__3_ on blockimpl0_.ID=blockimpl0__3_.BLOCK_ID where blockimpl0_.ID=?
Block14: cz.presto.didacticus.study.Block14Impl@fe5757cb class: cz.presto.didacticus.study.Block14$$EnhancerByCGLIB$$318e98d8

Time: 10,17

OK (1 test)


Thank you very much for your time.
Pavel


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 32 posts ]  Go to page 1, 2, 3  Next

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.