-->
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.  [ 12 posts ] 
Author Message
 Post subject: first level cache
PostPosted: Thu Aug 04, 2005 1:15 pm 
Is there a way to tell NHibernate not to return the object from the first level cache for certain objects?

The problem is that I have several objects, because of FKPK one-to-one relationships and joined subclasses that share the same Guid key. If i retreive an object with a key, NHibernate always returns the same object from the first level cache even when I query for another object with the same key, and I get errors when it tries to cast them.

Below is the exception form the log:
Code:
Exception: NHibernate.WrongClassException
Message: Object with id: 612a3bd7-45ee-4f9f-8621-065f14bb9368 was not of the specified sublcass: Owner (loading object was of wrong class)
Source: NHibernate
   at NHibernate.Loader.Loader.InstanceAlreadyLoaded(IDataReader rs, Int32 i, ILoadable persister, String suffix, Key key, Object obj, LockMode lockMode, ISessionImplementor session)
   at NHibernate.Loader.Loader.GetRow(IDataReader rs, ILoadable[] persisters, String[] suffixes, Key[] keys, Object optionalObject, Key optionalObjectKey, LockMode[] lockModes, IList hydratedObjects, ISessionImplementor session)
   at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, IList hydratedObjects, Object optionalObject, Object optionalId, Key[] keys, Boolean returnProxies)
   at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Object optionalObject, Object optionalId, Object[] optionalCollectionKeys, Boolean returnProxies)
   at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Object optionalObject, Object optionalId, Object[] optionalCollectionKeys, Boolean returnProxies)
   at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
   at NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet querySpaces, IType[] resultTypes)
   at NHibernate.Loader.CriteriaLoader.List(ISessionImplementor session)
   at NHibernate.Impl.SessionImpl.Find(CriteriaImpl criteria)


Top
  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 11:45 am 
Newbie

Joined: Fri Jul 29, 2005 11:48 am
Posts: 11
Now this is really annoying i keep getting this:
Quote:
another object with the same id was already associated with the session


Is there no way around this?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 12:58 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
You can use session.Evict, but this indicates to me some problem in your design or mappings.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 1:09 pm 
Newbie

Joined: Fri Jul 29, 2005 11:48 am
Posts: 11
I know about session.Evict, but that means i have to keep a reference to object i don't want cached in the session, which is not possible all the time. I tried evicting the object right after retreiveing it but it causes bugs when i try to access collection properties of the object.

This problem is happening because I'm using joined subclasses, which means the parent class and the subclass have the same id. I don't think that is a problem in my design or mapping. NHibernate or the cache should be smart enough to let me fetching another class even though it has the same id.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 1:52 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
I don't quite understand this - how does it happen that the base class becomes associated with the id when only the derived class should?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 2:59 pm 
I have a base Person class, and 2 derived classes called Owner and Investor. They are mapped as joined-subclasses. In my code if I retreive the Owner object first, then I try to retreive the Investor object I get the errors. I think its getting the owner object from session because it sees it has the same id, and is trying to cast it to an investor object.

Another case when this happends is if I have a one-to-one FKPK mapping between 2 classes. Both objects would have the same id, even though one is not derived from the other. The same things happens if I try to get a second object, and it has the same id as an object already in the session.

Hope this helps explain my problem. Let me know if you need more infomation. Thanks for trying to help.


Top
  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 3:41 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Well, tell me if I'm wrong, but I think Owners and Investors shouldn't be mapped as joined subclasses, because they aren't subclasses at all, they are roles. See this Wikipedia article for example.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 05, 2005 4:27 pm 
Newbie

Joined: Fri Jul 29, 2005 11:48 am
Posts: 11
Owners and investors don't preform any actions. They are just data, and they share some common properties. They don't change either, once an owner, always an owner. Its the same for investors. That's why I modeled them as subclasses. I believe that is correct.

But this is getting away from the caching problem I'm experiencing. Do you believe there is a problem with NHibernate, and do you know of a solution?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 06, 2005 8:24 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Well you can of course consider it a problem in NHibernate, but NHibernate just can't handle any data schema you throw at it, or it would end up being much more complex. Now if you could show us your classes, mappings, and the code that causes the problem, maybe we could offer some advice on how to avoid it.

As far as I understand, you have an object that is both an Owner and an Investor? (You say you retrieve an owner with an id, and then you try to retrieve an investor with the same id, that's where it fails.) If it's so, NHibernate won't help you, because you essentially have multiple inheritance there (or roles), and you can't map them as subclasses. You could try changing it like this:

Code:
public class Owner
{
    public Person Person { ... };
}

public class Investor
{
    public Person Person { ... };
}


and map the references to Person as many-to-one or one-to-one associations. Or another way would be:

Code:
public class Person
{
    ....
    public Investor InvestorRole { ... }; // may be null
    public Owner OwnerRole { ... }; // may be null
}


and map those roles as one-to-ones or many-to-ones again.

If I misunderstood your problem, then please provide some code and mappings, it should clear things up.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 08, 2005 10:30 am 
Newbie

Joined: Fri Jul 29, 2005 11:48 am
Posts: 11
The problem not only happens with joined-subclasses. It also happends with one-to-one PKFK mappings.

My guess is that NHibernate caches the objects without their type information, and relies on their Id's to distinguish them. Maybe this only happens with Guid keys. Maybe NHibernate assumes that Guid keys are always unique between objects and doesn't check their type, but that's not the case if you have one-to-one mapping and joined-subclass mappings.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 08, 2005 12:40 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
NHibernate considers the type when caching the objects, so what you describe sounds like a bug to me. If you can provide a simple self-contained test case (mappings, classes, code), I'll look at it.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 08, 2005 5:34 pm 
Newbie

Joined: Fri Jul 29, 2005 11:48 am
Posts: 11
First, I like to thank you for your help sergey.

I switched to version 0.9.0.0 from 0.8.4.0 last friday, and I couldn't replicate the error I was having. My code base and mappings have changed too since my first post on Aug 4, so that could explain it too. I might create test case later when I have time for my sanity sake anyways.

Below are my classes and mappings.
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" >
   <class name="Person" table="Person" dynamic-insert="true" dynamic-update="true">
      
      <id name="Id" column="PersonId" type="Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
         <generator class="guid.comb"/>
      </id>
      <property column="Prefix" type="String" name="Prefix" length="10" />
      <property column="FirstName" type="String" name="FirstName" length="50" />
      <property column="MiddleInitial" type="String" name="MiddleInitial" length="10" />
      <property column="LastName" type="String" name="LastName" length="50" />
      <property column="Salutation" type="String" name="Salutation" length="50" />
      <property column="Title" type="String" name="Title" length="50" />
      <property column="Company" type="String" name="Company" length="50" />
.....
   </class>
</hibernate-mapping>

Code:
using System;
using System.Collections;

namespace Data
{

   /// <summary>
   ///   
   /// </summary>
   [Serializable]
   public class Person : BusinessDataBase
   {
      private Guid personId;
      private string prefix;
      private string firstName;
      private string middleInitial;
      private string lastName;
      private string salutation;
      private string title;
      private string company;

      /// <summary>
      /// default constructor
      /// </summary>
      public Person()
      {
      
      }


      public override Guid Id
      {
         get { return personId; }
         set { personId = value; }
      }
      
      public virtual string Prefix
      {
         get { return prefix; }
         set { prefix = value; }
      }
      
      public virtual string FirstName
      {
         get { return firstName; }
         set { firstName = value; }
      }
      
      public virtual string MiddleInitial
      {
         get { return middleInitial; }
         set { middleInitial = value; }
      }
      
      public virtual string LastName
      {
         get { return lastName; }
         set { lastName = value; }
      }
      
      public virtual string Salutation
      {
         get { return salutation; }
         set { salutation = value; }
      }
      
      public virtual string Title
      {
         get { return title; }
         set { title = value; }
      }
      
      public virtual string Company
      {
         get { return company; }
         set { company = value; }
      }

....

   }
}

Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
   <joined-subclass name="Owner" table="Owner" extends="Person">
      <key column="OwnerId" />
      <bag name="Properties" lazy="true" table="PropertyOwned" cascade="all" inverse="true">
         <key column="OwnerId" />
         <composite-element class="PropertyOwned">
            <parent name="Owner" />
            <property column="IsPrimaryOwner" type="Nullables.NHibernate.NullableBooleanType, Nullables.NHibernate" name="IsPrimaryOwner" />
            <property column="FromDate" type="Nullables.NHibernate.NullableDateTimeType, Nullables.NHibernate" name="FromDate" />
            <property column="ToDate" type="Nullables.NHibernate.NullableDateTimeType, Nullables.NHibernate" name="ToDate" />
            <property column="OwnerType" type="String" name="OwnerType" length="50" />
            <property column="NameOnFile" type="String" name="NameOnFile" length="50" />
            <many-to-one name="Property" column="PropertyId" class="Property" />
         </composite-element>
      </bag>
   </joined-subclass>
</hibernate-mapping>

Code:
using System;
using System.Collections;

namespace Data
{
   /// <summary>
   /// Summary description for Owner.
   /// </summary>
   [Serializable]
   public class Owner : Person
   {
      private IList properties = new ArrayList();
      
      public Owner()
      {

      }
       

      public virtual IList Properties
      {
         get { return properties; }
         set { properties = value; }
      }
   }
}

Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
   <joined-subclass name="Investor" table="Investor" extends="Person" dynamic-insert="true" dynamic-update="true">
      <key column="InvestorId" />
      <property name="Type" column="InvestorType" type="String" length="50" />
      <property name="Areas" column="Areas" type="String" length="500" />
      <property name="Ranges" column="Ranges" type="String" length="500" />
      <property name="Types" column="Types" type="String" length="500" />
      <property name="Activities" column="Activities" type="String" length="500" />
      <bag name="Offers" lazy="true" inverse="true">
         <key column="InvestorId" />
         <one-to-many class="Offer" />
      </bag>
   </joined-subclass>
</hibernate-mapping>

Code:
using System;
using System.Collections;

namespace Data
{
   /// <summary>
   /// Summary description for Investor.
   /// </summary>
   [Serializable]
   public class Investor : Person
   {

      private string type;
      private string areas;
      private string ranges;
      private string types;
      private string activities;
      private IList offers = new ArrayList();

      public Investor()
      {
         //
         // TODO: Add constructor logic here
         //
      }

      public virtual string Type
      {
         get { return type; }
         set { type = value; }
      }

      public virtual string Areas
      {
         get { return areas; }
         set { areas = value; }
      }

      public virtual string Ranges
      {
         get { return ranges; }
         set { ranges = value; }
      }

      public virtual string Types
      {
         get { return types; }
         set { types = value; }
      }

      public virtual string Activities
      {
         get { return activities; }
         set { activities = value; }
      }

      public virtual IList Offers
      {
         get { return offers; }
         set { offers = value; }
      }
   }
}


Mapping Owners and Investors as subclasses makes building searches a lot easier and makes working with them easier too in my program.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 12 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.