-->
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.  [ 11 posts ] 
Author Message
 Post subject: Inheritance Problem
PostPosted: Fri May 18, 2007 5:56 am 
Beginner
Beginner

Joined: Thu Jan 25, 2007 3:36 pm
Posts: 25
Hello,

Firstly, apologies for the duplication - I was originally posting to Hibernate Users and have just realised that was probably the wrong forum for this problem (http://forum.hibernate.org/viewtopic.php?t=974543), so I'm re-posting here...


Hibernate version: 3.2.2
Hibernate Annotations version: 3.3.0.GA
Hibernate EntityManager version: 3.3.0.GA
Database: MySql 5.0.27
JRE: 1.5.0_07 (Java HotSpot(TM) Server VM (build 1.5.0_07-87, mixed mode))


I'm having a problem loading objects where an inheritance hierarchy is involved. I have three entity types:
    PlayerAccount
    WalletPlayerAccount extends PlayerAccount
    InternalPlayerAccount extends WalletPlayerAccount

None of these are abstract - an entity can be one of any of these three types.

The ORM specifies a Joined inheritance strategy:

Code:
<entity class="com.blah.PlayerAccount" access="FIELD">
   <inheritance strategy="JOINED" />

    <attributes>
        <id name="id">
            <generated-value strategy="AUTO" />
        </id>

        <basic name="firstName" optional="false" fetch="EAGER" />
        <basic name="surname" optional="false" fetch="EAGER" />
        [...snip...]
    </attributes>
</entity>

<entity class="com.blah.WalletPlayerAccount" access="FIELD">
    <attributes>
        <basic name="walletBalance" optional="false" fetch="EAGER" />
    </attributes>
</entity>


<entity class="com.blah.InternalPlayerAccount" access="FIELD">
    <attributes>
        [...snip - more stuff that's not very interesting]
    </attributes>
</entity>



I have two accounts in the database:
    Account with ID 1 is an InternalPlayerAccount (i.e. there is a row in each of the tables PlayerAccount, WalletPlayerAccount and InternalPlayerAccount, all of which have id=1).

    Account with ID 2 is a WalletPlayerAccount (i.e. there is a row in each of the tables PlayerAccount and WalletPlayerAccount which both have id=2, but no row exists in InternalPlayerAccount with this id).
Note that both these accounts have been created via Hibernate (i.e. via instantiation of new objects and then calling EntityManager.persist()) - they do not exist as a result of dodgy hacking direct to database, so I'm sure there isn't a data bug.

Now, if I start my application and invoke some tests that cause repeated loading of the account with ID=1 then it always comes back correctly as an InternalPlayerAccount.

If I then invoke a test that causes the loading of the account with ID=2 then it comes back correctly as a WalletPlayerAccount.

So far so good. Now, if I try to reload the account with ID=1 it comes back as a WalletPlayerAccount, not an InternalPlayerAccount. I can check the database and nothing has changed between the finds. Furthermore if I restart the application then the account is loaded as an InternalPlayerAccount again (until I load a WalletPlayerAccount that is, when the problem resurfaces again).

I can't understand what I'm doing wrong, especially as the entity with ID=1 loads properly until the object with ID=2 is first loaded. I have deliberately missed out a few things from the code above which I don't believe are relevant, but will happily be more verbose if anyone wants more info.

Many thanks
Darren


...followup post...


I'm still investigating this and have some more diagnostics.

Firstly, when performing the eager find that's causing the problem, I was using a named-query that performed LEFT JOIN FETCHes for PlayerAccount's lazy relationships and then calling Hibernate.initialize() to load the subclass lazy relationships. I've replaced the named-query execution with an EntityManager.find( Class, id ) (again followed by a bunch of Hibernate.initialize() calls where necessary) to see if that was causing the problem, but it has made no difference.

So, I inspected the SQL that Hibernate generates and found there is no difference between the SQL generated on the first call (which succeeds) and the last call (which returns the wrong type). For reference, here it is:

Code:
select  playeracco0_.id                                         as id13_1_,
        playeracco0_.accountSystem_id                           as accountS6_13_1_,
        playeracco0_.failedLoginAttemptCount                    as failedLo2_13_1_,
        playeracco0_.locked                                     as locked13_1_,
        playeracco0_.preferredOutlet_id                         as preferre7_13_1_,
        playeracco0_.suspended                                  as suspended13_1_,
        playeracco0_.username                                   as username13_1_,
        playeracco0_1_.nonWithdrawableBalance                   as nonWithd2_14_1_,
        playeracco0_1_.withdrawableBalance                      as withdraw3_14_1_,
        playeracco0_2_.accountNumber                            as accountN2_15_1_,
        playeracco0_2_.accountSystemId                          as accountS3_15_1_,
        playeracco0_2_.address_id                               as address21_15_1_,
        playeracco0_2_.challengeAnswer                          as challeng4_15_1_,
        playeracco0_2_.challengeQuestion_id                     as challen20_15_1_,
        playeracco0_2_.contactableByOthers                      as contacta5_15_1_,
        playeracco0_2_.contactableByUs                          as contacta6_15_1_,
        playeracco0_2_.dob                                      as dob15_1_,
        playeracco0_2_.email                                    as email15_1_,
        playeracco0_2_.encPin                                   as encPin15_1_,
        playeracco0_2_.firstName                                as firstName15_1_,
        playeracco0_2_.lastName                                 as lastName15_1_,
        playeracco0_2_.over18                                   as over12_15_1_,
        playeracco0_2_.paymentCard_id                           as payment19_15_1_,
        playeracco0_2_.pinIsOneShot                             as pinIsOn13_15_1_,
        playeracco0_2_.registrationDate                         as registr14_15_1_,
        playeracco0_2_.significantDate                          as signifi15_15_1_,
        playeracco0_2_.telephone                                as telephone15_1_,
        playeracco0_2_.termsAndCondsAccepted                    as termsAn17_15_1_,
        playeracco0_2_.title                                    as title15_1_,
        case    when playeracco0_2_.id is not null then 2
                when playeracco0_1_.id is not null then 1
                when playeracco0_.id is not null then 0 end     as clazz_1_,
        accountsys1_.id                                         as id3_0_,
        accountsys1_.adapterClass                               as adapterC2_3_0_,
        accountsys1_.encAdapterClassConfigXml                   as encAdapt3_3_0_,
        accountsys1_.internal                                   as internal3_0_,
        accountsys1_.name                                       as name3_0_,
        accountsys1_.supportsCashDeposits                       as supports6_3_0_,
        accountsys1_.supportsReconciliation                     as supports7_3_0_,
        accountsys1_.tag                                        as tag3_0_

from            PlayerAccount       playeracco0_
left outer join WalletPlayerAccount playeracco0_1_ on playeracco0_.id=playeracco0_1_.id
left outer join InternalPlayerAccount playeracco0_2_ on playeracco0_.id=playeracco0_2_.id
inner join      AccountSystem accountsys1_ on playeracco0_.accountSystem_id=accountsys1_.id
where           playeracco0_.id=?



Next, I stepped through in the debugger...
    1) the first find (an InternalPlayerAccount with ID=1) succeeded as before (repeated 5 times, no problem)
    2) the next find (a WalletPlayerAccount with ID=2) succeeded as before (repeated 5 times, no problem)
    3) the next find (the InternalPlayerAccount with ID=1 again) failed on the first attempt. (Actually, as explained previously, it doesn't *fail* but it returns an entity of type WalletPlayerAccount instead of InternalPlayerAccount.
Whilst in the debugger and immediately prior to step (3) above, I switched to a query tool and executed the SQL above to see if bad data was being returned (specifically whether nulls are returned for the InternalPlayerAccount columns), but everything looked good and valid data was returned from all three *Account tables.


In case it helps, here is the Hibernate log output. This first extract corresponds to step (1) in the list above, i.e. a successful find:
Code:
2007-05-17 10:40:59.265 FINE    org.hibernate.SQL       select playeracco0_.id as id13_1_,  [snip - see SQL above]
2007-05-17 10:40:59.268 FINE    org.hibernate.jdbc.AbstractBatcher      about to open ResultSet (open ResultSets: 0, globally: 0)
2007-05-17 10:40:59.269 FINE    org.hibernate.loader.Loader     result set row: 0
2007-05-17 10:40:59.271 FINE    org.hibernate.loader.Loader     result row: EntityKey[com.evolvegaming.lgp.AccountSystem#1], EntityKey[com.evolvegaming.lgp.PlayerAccount#1]
2007-05-17 10:40:59.278 FINE    org.hibernate.jdbc.AbstractBatcher      about to close ResultSet (open ResultSets: 1, globally: 1)
2007-05-17 10:40:59.278 FINE    org.hibernate.jdbc.AbstractBatcher      about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2007-05-17 10:40:59.278 FINE    org.hibernate.jdbc.ConnectionManager    aggressively releasing JDBC connection
2007-05-17 10:40:59.279 FINE    org.hibernate.jdbc.ConnectionManager    releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
2007-05-17 10:40:59.279 FINE    org.hibernate.engine.TwoPhaseLoad       resolving associations for [com.evolvegaming.lgp.AccountSystem#1]
2007-05-17 10:40:59.280 FINE    org.hibernate.engine.TwoPhaseLoad       done materializing entity [com.evolvegaming.lgp.AccountSystem#1]
2007-05-17 10:40:59.280 FINE    org.hibernate.engine.TwoPhaseLoad       resolving associations for [com.evolvegaming.lgp.InternalPlayerAccount#1]
2007-05-17 10:40:59.282 FINE    org.hibernate.engine.TwoPhaseLoad       done materializing entity [com.evolvegaming.lgp.InternalPlayerAccount#1]
2007-05-17 10:40:59.282 FINE    org.hibernate.engine.StatefulPersistenceContext initializing non-lazy collections
2007-05-17 10:40:59.282 FINE    org.hibernate.loader.Loader     done entity load
2007-05-17 10:40:59.283 FINE    org.hibernate.jdbc.ConnectionManager    aggressively releasing JDBC connection



This second extract corresponds to step (3) in the list above, i.e. a find that returns an entity of the incorrect type.
Code:
2007-05-17 10:41:00.771 FINE    org.hibernate.SQL       select playeracco0_.id as id13_1_, [snip - see SQL above]
2007-05-17 10:41:00.773 FINE    org.hibernate.jdbc.AbstractBatcher      about to open ResultSet (open ResultSets: 0, globally: 0)
2007-05-17 10:41:00.774 FINE    org.hibernate.loader.Loader     result set row: 0
2007-05-17 10:41:00.774 FINE    org.hibernate.loader.Loader     result row: EntityKey[com.evolvegaming.lgp.AccountSystem#1], EntityKey[com.evolvegaming.lgp.PlayerAccount#1]
2007-05-17 10:41:00.777 FINE    org.hibernate.jdbc.AbstractBatcher      about to close ResultSet (open ResultSets: 1, globally: 1)
2007-05-17 10:41:00.778 FINE    org.hibernate.jdbc.AbstractBatcher      about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2007-05-17 10:41:00.778 FINE    org.hibernate.jdbc.ConnectionManager    aggressively releasing JDBC connection
2007-05-17 10:41:00.778 FINE    org.hibernate.jdbc.ConnectionManager    releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
2007-05-17 10:41:00.779 FINE    org.hibernate.engine.TwoPhaseLoad       resolving associations for [com.evolvegaming.lgp.AccountSystem#1]
2007-05-17 10:41:00.779 FINE    org.hibernate.engine.TwoPhaseLoad       done materializing entity [com.evolvegaming.lgp.AccountSystem#1]
2007-05-17 10:41:00.780 FINE    org.hibernate.engine.TwoPhaseLoad       resolving associations for [com.evolvegaming.lgp.WalletPlayerAccount#1]
2007-05-17 10:41:00.781 FINE    org.hibernate.engine.TwoPhaseLoad       done materializing entity [com.evolvegaming.lgp.WalletPlayerAccount#1]
2007-05-17 10:41:00.781 FINE    org.hibernate.engine.StatefulPersistenceContext initializing non-lazy collections
2007-05-17 10:41:00.781 FINE    org.hibernate.loader.Loader     done entity load
2007-05-17 10:41:00.781 FINE    org.hibernate.jdbc.ConnectionManager    aggressively releasing JDBC connection



The most interesting bits to my mind are the last two org.hibernate.engine.TwoPhaseLoad entries in each extract - in the first extract it resolves/materializes InternalPlayerAccount and in the second extract WalletPlayerAccount.

I'm going to continue investigating, but if this is ringing bells for anyone, I'd really appreciate any thoughts/comments/suggestions.

Darren


...followup post...


I think I'm getting somewhere... as I said previously, I'm reproducing the problem consistently by performing the following steps:

    1) Finding an InternalPlayerAccount with ID=1 succeeds (repeated 5 times, no problem)
    2) Finding a WalletPlayerAccount with ID=2 succeeds (repeated 5 times, no problem)
    3) Finding the InternalPlayerAccount with ID=1 again returns an entity of type WalletPlayerAccount instead of InternalPlayerAccount on the first attempt.

and the actual logic looks something like this:
Code:
int outerLoopLimit = 5;
int innerLoopLimit = 5;
try {
    for( int h=0; h<outerLoopLimit; h++ ) {

        for( int i=0 ; i<innerLoopLimit; i++ ) {
            findPlayerAccount(1); //i.e. the InternalPlayerAccount with id=1
        }

        for( int i=0 ; i<innerLoopLimit; i++ ) {
            findPlayerAccount(2); //i.e. the WalletPlayerAccount with id=2
        }

    }
} catch( Throwable t ) {
    t.printStackTrace();
}


When I execute this, the code gets through the first inner loop all five times without a problem. It then gets through the second inner loop OK and starts to re-iterate the outer loop. It then fails on the first execution of findPlayerAccount(1).

So, I tried playing with the values of innerLoopLimit. If I increase it, the behaviour described above is always the same - first outer iteration succeeds, then the first iteration of the first inner loop fails.

However, if I decrease the innerLoopLimit to two, I find this isn't the case - loading the account with id=1 succeeds even on the second iteration of the outer loop until the 6th time in total, at which point it fails as before. I decrease the innerLoopLimit to one and see the same - I get 5 good finds of account with id=1 (even after loading a WalletPlayerAccount), then fail on the 6th.

This seemed fishy, I looked through my persistence.xml and noticed c3p0's connection pool min_size=5
Code:
  <property name="hibernate.c3p0.min_size"       value="5" />
  <property name="hibernate.c3p0.max_size"       value="20" />
  <property name="hibernate.c3p0.timeout"        value="1800" />
  <property name="hibernate.c3p0.max_statements" value="100" />



I tried removing the c3p0-related properties from configuration altogether and my problem dissapears!


So, does anybody think this could be a C3P0 bug, or could it still be something I'm doing wrong (in which case all suggestions appreciated!)

Darren


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 23, 2007 8:53 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
what is the code for findPlayeAccount()?

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 24, 2007 6:44 am 
Beginner
Beginner

Joined: Thu Jan 25, 2007 3:36 pm
Posts: 25
Code:
public PlayerAccount findPlayerAccount( int id ) throws Exception {
    PersistenceContextManager   pcm;
    EntityManager               em;
    Query                       query;
    PlayerAccount               playerAccount;


    pcm = LgpCorePersistenceContextManager.getInstance();
    em  = pcm.getReadOnlyEntityManager();
    try {
        query = LgpCorePersistenceContextManager
                .getQueryFindPlayerAccountWithPopulatedRelationships( em, id );
        playerAccount = (PlayerAccount) query.getSingleResult();

        if( playerAccount != null ) {
            //Account's lazy relationships will have been loaded, but
            //those related objects' won't have been so load them now:-
            if( playerAccount.getPreferredOutlet() != null ) {
                Hibernate.initialize(
                    playerAccount.getPreferredOutlet().getOutletManager() );
            }

            //Additional processing specific to internal accounts:-
            if( playerAccount instanceof InternalPlayerAccount ) {
                [snip - some more Hibernate.initialize() calls]
            }

            return playerAccount;
        }
    } catch( NoResultException x ) {
        return null;
    } finally {
        pcm.close();
    }
}



In the cases I've described when the entity is returned properly as an InternalPlayerAccount, the code marked above with the [snip - some more Hibernate.initialize() calls] is executed. In later executions when the entity isn't returned correctly, that code is not executed as playerAccount instanceof InternalPlayerAccount returns false and the if-block is therefore not entered.


For completeness, called methods perform as follows (there is a fair amount of abstraction and nested methods involved to achieve what's described below so won't dump it all here unless you want me to)...
    LgpCorePersistenceContextManager.getInstance() uses a ThreadLocal to return an instance of the LgpCorePersistenceContextManager class if the calling thread hasn't already done so, or returns the same on if the calling thread already has one.

    LgpCorePersistenceContextManager.getReadOnlyEntityManager() returns the result of an EntityManagerFactory's createEntityManager().

    LgpCorePersistenceContextManager.getQueryFindPlayerAccountWithPopulatedRelationships() returns a Query with populated parameters based on the following QL:
    Code:
             SELECT DISTINCT pa
             FROM            PlayerAccount pa
             LEFT JOIN FETCH pa.accountSystem
             LEFT JOIN FETCH pa.preferredOutlet
             LEFT JOIN FETCH pa.notes
             WHERE           pa.id = :id



Top
 Profile  
 
 Post subject:
PostPosted: Thu May 24, 2007 8:19 am 
Beginner
Beginner

Joined: Thu Jan 25, 2007 3:36 pm
Posts: 25
By the way, I'm confident that the logic for ThreadLocal-based session management is working OK.

If I trawl the logs, I find that every JDBC connection that gets opened is always closed, i.e. for every

Code:
FINE   org.hibernate.jdbc.ConnectionManager   opening JDBC connection

there follows a

Code:
FINE   org.hibernate.jdbc.ConnectionManager   aggressively releasing JDBC connection


and for every

Code:
FINE   org.hibernate.impl.SessionImpl   opened session at timestamp: 118000nnnnn

there follows a

Code:
FINEST   org.hibernate.impl.SessionImpl   closing session


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 24, 2007 10:55 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
I don't see any obvious reason, try to minimize your test case and use Hibernate APIs only, all the imbrications are hard to debug

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 07, 2007 8:09 am 
Beginner
Beginner

Joined: Thu Jan 25, 2007 3:36 pm
Posts: 25
I've replicated the problem in a standalone example with much of the irrelevant complexity removed.

I have three entities Animal, Cat and Lion, as follows:

Code:
class Animal {
    private int    id;
    private String name;

    public Animal( String name ) {
        this.name = name;
    }

    [snip - default ctor and getters]
}



class Cat extends Animal {
    private int numLives;

    public Cat( String name, int numLives ) {
        super( name );
        this.numLives = numLives;
    }

    [snip - default ctor and getter]
}



class Lion extends Cat {
    private boolean isLeader;

    public Lion( String name, int numLives, boolean isLeader ) {
        super( name, numLives );
        this.isLeader = isLeader;
    }

    [snip - default ctor and getter]
}


The orm.xml and persistence.xml are straightforward:

ORM:
Code:
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">

   <entity class="Animal" access="FIELD" name="Animal">
      <inheritance strategy="JOINED" />

      <attributes>
         <id name="id">
            <generated-value strategy="AUTO" />
         </id>

         <basic name="name" optional="false" />
      </attributes>
   </entity>

   <entity class="Cat" access="FIELD">
      <attributes>
         <basic name="numLives" optional="false"   />
      </attributes>
   </entity>

   <entity class="Lion" access="FIELD">
      <attributes>
         <basic name="isLeader" optional="false" />
      </attributes>
   </entity>
</entity-mappings>


Persistence:
Code:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
   version="1.0">

   <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <mapping-file>META-INF/orm.xml</mapping-file>

      <properties>
         <property name="hibernate.dialect"      value="org.hibernate.dialect.MySQL5Dialect" />
         <property name="hibernate.hbm2ddl.auto" value="update" />

         <property name="hibernate.show_sql"     value="false" />

         <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
         <property name="hibernate.connection.url"          value="jdbc:mysql://localhost/lgpcore" />
         <property name="hibernate.connection.username"     value="darrenclarke" />
         <property name="hibernate.connection.password"     value="" />

         <!-- Connection pooling via C3P0 -->
         <property name="hibernate.c3p0.min_size"       value="5" />
         <property name="hibernate.c3p0.max_size"       value="20" />
         <property name="hibernate.c3p0.timeout"        value="1800" />
         <property name="hibernate.c3p0.max_statements" value="100" />
      </properties>
   </persistence-unit>
</persistence>



The code I execute to run the test is as follows:
Code:
import javax.persistence.*;

public class Main {

    static EntityManagerFactory entityManagerFactory =
        Persistence.createEntityManagerFactory( "test" );


    /**
     * Runs through a series steps to prove whether or not there's a problem
     * loading lions and cats.  Assumes there's a Lion with ID=1 and a Cat with
     * ID=2 in the database.
     */
    public static void main( String args[] ) {
        try {
            for( int h=0; h<2; h++ ) {
                System.out.println( "\n\n==== Outer Iteration " + h );

                for( int i=0 ; i<5; i++ ) {
                    System.out.print( "lion "+ i + "     " );
                    loadAnimal( 1 );
                }

                for( int i=0 ; i<5; i++ ) {
                    System.out.print( "cat  " + i + "     " );
                    loadAnimal( 2 );
                }
            }
        } catch( Throwable t ) {
           t.printStackTrace( );
        }
    }



    private static void loadAnimal( int id ) throws Exception {
        EntityManager   em;
        Animal          animal;


        em  = entityManagerFactory.createEntityManager();

        try {
            System.out.print( "Loading animal with ID=" + id + " ...  " );

            animal = em.find( Animal.class, id );

            System.out.println( "  id=" + animal.getId()
                + " , type=" + animal.getClass().getName() );

        } catch( Exception x ) {
            throw new Exception( "Unable to perform find", x );
        } finally {
            em.close();
        }

    }
}



So, I search for the animal with ID=1 (which is a Lion in the database) 5 times, then do the same for the animal with ID=2 (which is a Cat in the database). I then repeat the entire process.

If I run without C3P0 (i.e. comment out all c3p0-related settings in persistence.xml) I have no problems and my output looks like this:

Code:
==== Outer Iteration 0
lion 0     Loading animal with ID=1 ...    id=1 , type=Lion
lion 1     Loading animal with ID=1 ...    id=1 , type=Lion
lion 2     Loading animal with ID=1 ...    id=1 , type=Lion
lion 3     Loading animal with ID=1 ...    id=1 , type=Lion
lion 4     Loading animal with ID=1 ...    id=1 , type=Lion
cat  0     Loading animal with ID=2 ...    id=2 , type=Cat
cat  1     Loading animal with ID=2 ...    id=2 , type=Cat
cat  2     Loading animal with ID=2 ...    id=2 , type=Cat
cat  3     Loading animal with ID=2 ...    id=2 , type=Cat
cat  4     Loading animal with ID=2 ...    id=2 , type=Cat


==== Outer Iteration 1
lion 0     Loading animal with ID=1 ...    id=1 , type=Lion
lion 1     Loading animal with ID=1 ...    id=1 , type=Lion
lion 2     Loading animal with ID=1 ...    id=1 , type=Lion
lion 3     Loading animal with ID=1 ...    id=1 , type=Lion
lion 4     Loading animal with ID=1 ...    id=1 , type=Lion
cat  0     Loading animal with ID=2 ...    id=2 , type=Cat
cat  1     Loading animal with ID=2 ...    id=2 , type=Cat
cat  2     Loading animal with ID=2 ...    id=2 , type=Cat
cat  3     Loading animal with ID=2 ...    id=2 , type=Cat
cat  4     Loading animal with ID=2 ...    id=2 , type=Cat


I.e. the animal with ID=1 is always a Lion.

If I enable c3p0 (i.e. uncomment the settings in persistence.xml) then I get the same error previously described and the output looks like this:

Code:
==== Outer Iteration 0
lion 0     Loading animal with ID=1 ...    id=1 , type=Lion
lion 1     Loading animal with ID=1 ...    id=1 , type=Lion
lion 2     Loading animal with ID=1 ...    id=1 , type=Lion
lion 3     Loading animal with ID=1 ...    id=1 , type=Lion
lion 4     Loading animal with ID=1 ...    id=1 , type=Lion
cat  0     Loading animal with ID=2 ...    id=2 , type=Cat
cat  1     Loading animal with ID=2 ...    id=2 , type=Cat
cat  2     Loading animal with ID=2 ...    id=2 , type=Cat
cat  3     Loading animal with ID=2 ...    id=2 , type=Cat
cat  4     Loading animal with ID=2 ...    id=2 , type=Cat


==== Outer Iteration 1
lion 0     Loading animal with ID=1 ...    id=1 , type=Cat
lion 1     Loading animal with ID=1 ...    id=1 , type=Cat
lion 2     Loading animal with ID=1 ...    id=1 , type=Cat
lion 3     Loading animal with ID=1 ...    id=1 , type=Cat
lion 4     Loading animal with ID=1 ...    id=1 , type=Cat
cat  0     Loading animal with ID=2 ...    id=2 , type=Cat
cat  1     Loading animal with ID=2 ...    id=2 , type=Cat
cat  2     Loading animal with ID=2 ...    id=2 , type=Cat
cat  3     Loading animal with ID=2 ...    id=2 , type=Cat
cat  4     Loading animal with ID=2 ...    id=2 , type=Cat


So, as before, my Lions load as Lions until I've loaded some animals that are only Cats. From that point on, my Lions are also loaded as Cats, even though nothing has changed in the DB. Comment out the C3P0 settings at this point and everything works again.

I can post the actual files somewhere (i.e. java and xml) if that would help.

Thanks
Darren


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 08, 2007 10:31 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
you must use find() with the exact class type.
If you don't know the type, then you'll have to use a query
Code:
from Animal where id = :id

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 08, 2007 11:05 am 
Beginner
Beginner

Joined: Thu Jan 25, 2007 3:36 pm
Posts: 25
As I don't the type in advance, I've replaced the find() with a query, but get exactly the same results.

FYI, the code now reads

Code:
...
//animal = em.find( Animal.class, id );
query = em.createNamedQuery( "findAnimal" );
query.setParameter( "id", id );
animal = (Animal) query.getSingleResult();
...


and the query is defined in the ORM as:

Code:
<named-query name="findAnimal">
   <query>
      SELECT a
      FROM   Animal a
      WHERE  a.id = :id
   </query>
</named-query>


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 08, 2007 5:42 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Then I'm short of explanation :)

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 12, 2007 9:25 am 
Beginner
Beginner

Joined: Thu Jan 25, 2007 3:36 pm
Posts: 25
Any ideas where I could go next, or what I could do to help get this diagnosed/fixed?

Thanks,
Darren


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 28, 2007 12:26 pm 
Beginner
Beginner

Joined: Thu Jan 25, 2007 3:36 pm
Posts: 25
I tried tracing through the C3P0 code to see if there was something obvious, but nothing jumped out - I've posted an issue in SourceForge on the C3P0 project to see if problem is of interest over there.

A bit of a breakthrough today though - I tried changing the inheritance strategy to SINGLE_TABLE to see if that makes a difference and the problem goes away. I.e. in my ORM, I changed this:

Code:
    <entity class="Animal" access="FIELD" name="Animal">
        <inheritance strategy="JOINED" />
        ...

to this

Code:
    <entity class="Animal" access="FIELD" name="Animal">
        <inheritance strategy="SINGLE_TABLE" />
        ...


So the bug is particular to the JOINED inheritance strategy.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 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:
cron
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.