-->
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.  [ 10 posts ] 
Author Message
 Post subject: Caching problem with parent pointers in collections
PostPosted: Wed Nov 02, 2005 2:46 pm 
Newbie

Joined: Tue Oct 11, 2005 3:18 pm
Posts: 16
I have 2 classes, A and B, of which A contains a one-to-many bag of B, and B contains a many-to-one property of parent type A, and caching is on using BantamTech (or NCache, doesn't matter which).

Test 1) Run the app, start a new configuration and session, and run first HQL returning A1, the bag of B1 is good, and B1 parent pointer back to A1 is good.

Test 2) Run the app, start a new configuration and session, and run second HQL returning A2, the bag of B2 is good, and B2 parent pointer back to A2 is good.

Test 3) Run the app, start a new configuration, run those same two HQL back to back in separate sessions, the first HQL results are good, but the second HQL results are bad. The bag of B2 somehow has parent pointers back to A1, instead of A2. I found two workarounds.... When I turn caching off on those 2 classes, then all results are good. If I wrap those 2 HQL within one transaction, then all results are good. But somehow when caching is on, and each HQL is run in separate session, but within the same configuration, then the parent pointer results from second HQL are bad.

I'm not convinced that the BantamTech plugin contribution is to blame, because the bad results also happen when using a NCache plugin from Alachisoft. So it may be a problem with how NHibernate interfaces with caching in general.

Any thoughts on this?

(Admin: I originally post this on the contrib/tools forum, but I would rather discuss this here instead.... you may delete my post in the other forum if you would like)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 10:58 am 
Newbie

Joined: Tue Oct 11, 2005 3:18 pm
Posts: 16
More info (class A is Deal, class B is DealVolume).....

Here is the first HQL:

from BP.Viper.DataObjects.Deal d where (d.Dealid = ( select MAX(i.Dealid) from BP.Viper.DataObjects.DealVolume i))

Here is the second HQL:

from BP.Viper.DataObjects.Deal d where (d.Dealid = ( select MAX(i.Dealid) from BP.Viper.DataObjects.DealVolume i where i.Dealid < 9217210631387887616))

{9217210631387887616 happens to be the Dealid returned by the first HQL}

Basically, the first HQL is returning the Deal object with the largest Dealid and that contains DealVolumes. The second HQL is returning the Deal object with the second-largest Dealid and that contains DealVolumes. However, like I said, the Deal object returned by the second HQL has a non-lazy bag of DealVolumes, and each DealVolume has a property of type Deal which should contain a reference to the parent, but it actually contains a reference to the parent from the first HQL. Not good.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 11:30 am 
Contributor
Contributor

Joined: Thu May 12, 2005 9:45 am
Posts: 593
Location: nhibernate.org
What do you mean by "start a new configuration"?

Anyway, I don't know where the problem is...
Can you create an independent test case so that we can test that on our machines? (and use it when creating the JIRA issue if it is a bug).

Admin: I deleted the old topic and moved this one here as it is mainly related to the caches (even if the problem is in NHibernate).

_________________
Pierre Henri Kuaté.
Get NHibernate in Action Now!


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 1:36 pm 
Newbie

Joined: Tue Oct 11, 2005 3:18 pm
Posts: 16
I should have said "instantiate a configuration"... ie. NHibernate.Cfg.Configuration configuration = new NHibernate.Cfg.Configuration();

I'll have to create an independent test case... will take some time... meanwhile here are my mappings/classes...

Here is the Deal xml.....

Code:
    <class name="BP.Viper.DataObjects.Deal, BP.Viper.DataObjects" table="Deal">
      <jcs-cache usage="read-write"/>
      <id name="Dealid" unsaved-value="0" column="DealId" type="long">
         <generator class="assigned" >
         </generator>
      </id>      
      <property name="Fromdate" column="FromDate" type="date"/>
      <property name="Todate" column="ToDate" type="date"/>
      <property name="Companyid" column="CompanyId" type="long"/>
      <property name="Contractid" column="ContractId" type="long"/>
      <property name="Uomid" column="UOMId" type="string"/>
      <property name="Currencyid" column="CurrencyId" type="string"/>
      <property name="Entityid" column="EntityId" type="long"/>
      <property name="Portfolioid" column="PortfolioId" type="string"/>
      <property name="Tradedate" column="TradeDate" type="date"/>
      <property name="Buysellid" column="BuySellId" type="string"/>
      <many-to-one name="Portfolio" column="PortfolioId" class="BP.Viper.DataObjects.Portfolio, BP.Viper.DataObjects" insert="false" update="false"/>
      <many-to-one name="Counterparty" column="CompanyId" class="BP.Viper.DataObjects.Counterparty, BP.Viper.DataObjects" insert="false" update="false"/>
      <many-to-one name="LegalEntity" column="EntityId" class="BP.Viper.DataObjects.LegalEntity, BP.Viper.DataObjects" insert="false" update="false"/>
      <bag name="Volumes" lazy="false" inverse="true" cascade="all">
         <key column="Dealid"/>
         <one-to-many class="BP.Viper.DataObjects.DealVolume, BP.Viper.DataObjects"/>
      </bag>
   </class>


Here is the DealVolume xml....

Code:
    <class name="BP.Viper.DataObjects.DealVolume, BP.Viper.DataObjects" table="DealVolume">
      <jcs-cache usage="read-write"/>
      <composite-id>
         <key-property name="Dealid" column="DealId" type="long"/>
         <key-property name="Point" column="Point" type="string" />
         <key-property name="Fromdate" column="FromDate" type="date" />
      </composite-id>
      <many-to-one name="Deal" column="DealId" class="BP.Viper.DataObjects.Deal,BP.Viper.DataObjects" insert="false" update="false" />
      <property name="Todate" column="ToDate" type="date"/>
      <property name="Quantity" column="Quantity" type="double"/>
      <property name="Uomid" column="UOMId" type="string"/>
   </class>


Here is the Deal class....

Code:
public class Deal {
    virtual public long Companyid{
        get    {
            return companyid;
        }
        set    {
            this.companyid = value;
        }
    }
    virtual public long Contractid{
        get    {
            return contractid;
        }
        set    {
            this.contractid = value;
        }
    }
    virtual public string Currencyid{
        get    {
            return currencyid;
        }
        set    {
            this.currencyid = value;
        }
    }
    virtual public long Dealid{
        get    {
            return dealid;
        }
        set    {
            this.dealid = value;
        }
    }
    virtual public long Entityid{
        get    {
            return entityid;
        }
        set    {
            this.entityid = value;
        }
    }
    virtual public System.DateTime Fromdate{
        get    {
            return fromdate;
        }
        set    {
            this.fromdate = value;
        }
    }
    virtual public string Portfolioid{
        get    {
            return portfolioid;
        }
        set    {
            this.portfolioid = value;
        }
    }
    virtual public System.DateTime Todate{
        get    {
            return todate;
        }
        set    {
            this.todate = value;
        }
    }
    virtual public System.DateTime Tradedate{
        get    {
            return tradedate;
        }
        set    {
            this.tradedate = value;
        }
    }
    virtual public string Uomid{
        get    {
            return uomid;
        }
        set    {
            this.uomid = value;
        }
    }
    virtual public string Buysellid {
        get {
            return buysellid;
        }
        set {
            this.buysellid = value;
        }
    }
    virtual public Portfolio Portfolio {
        get {
            return portfolio;
        }
        set {
            this.portfolio = value;
        }
    }
    virtual public Counterparty Counterparty {
        get {
            return counterparty;
        }
        set {
            this.counterparty = value;
        }
    }
    virtual public LegalEntity LegalEntity {
        get {
            return legalentity;
        }
        set {
            this.legalentity = value;
        }
    }
    virtual public IList Volumes {
        get {
            if (volumes==null) {
                 volumes = new ArrayList();
             }
            return volumes;
        }
        set {
            this.volumes = value;
        }
    }
    private long companyid;
    private long contractid;
    private string currencyid;
    private long dealid;
    private long entityid;
    private System.DateTime fromdate;
    private string portfolioid;
    private System.DateTime todate;
    private System.DateTime tradedate;
    private string uomid;
    private string buysellid;
    private Portfolio portfolio;
    private Counterparty counterparty;
    private LegalEntity legalentity;
    private IList volumes;
    public Deal(){
    }
    public Deal(long dealid){
        this.dealid = dealid;
    }
}


Here is the DealVolume class.....

Code:
public class DealVolume {
    virtual public Deal Deal    {
        get        {
            return deal;
        }
        set        {
            this.deal = value;
        }
    }
    virtual public long Dealid {
        get {
            return dealid;
        }
        set {
            this.dealid = value;
        }
    }
    virtual public System.DateTime Fromdate        {
        get            {
            return fromdate;
        }
        set            {
            this.fromdate = value;
        }
    }
    virtual public string Point        {
        get            {
            return point;
        }
        set            {
            this.point = value;
        }
    }
    virtual public double Quantity        {
        get            {
            return quantity;
        }
        set            {
            this.quantity = value;
        }
    }
    virtual public System.DateTime Todate        {
        get            {
            return todate;
        }
        set            {
            this.todate = value;
        }
    }
    virtual public string Uomid        {
        get            {
            return uomid;
        }
        set            {
            this.uomid = value;
        }
    }
    private long dealid;
    private System.DateTime fromdate;
    private string point;
    private double quantity;
    private System.DateTime todate;
    private string uomid;
    private Deal deal;
    public DealVolume()        {
    }
    public DealVolume(long dealid, System.DateTime fromdate, string point)        {
        this.dealid = dealid;
        this.fromdate = fromdate;
        this.point = point;
    }
    public override bool Equals(object obj) {
        if (obj == null)
            return false;
        return ((this.dealid == ((DealVolume)obj).dealid)&&
        (this.fromdate.ToShortDateString().Equals(((DealVolume)obj).fromdate.ToShortDateString()))&&
        (this.point.Equals(((DealVolume)obj).point)));
    }
    public override int GetHashCode() {
        return base.GetHashCode ();
    }
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 3:13 pm 
Newbie

Joined: Tue Oct 11, 2005 3:18 pm
Posts: 16
Here is sql server table ddl....

Code:
CREATE TABLE [Deal] (
   [DealId] [bigint] NOT NULL ,
   [PortfolioId] [varchar] (40) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
   [TradeDate] [datetime] NOT NULL ,
   [FromDate] [datetime] NOT NULL ,
   [ToDate] [datetime] NOT NULL ,
   [EntityId] [bigint] NOT NULL ,
   [CompanyId] [bigint] NOT NULL ,
   [ContractId] [bigint] NULL ,
   [CurrencyId] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
   [UOMId] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
   [BuySellId] [char] (1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
   CONSTRAINT [PK_Deal] PRIMARY KEY  CLUSTERED
   (
      [DealId]
   ) WITH  FILLFACTOR = 90  ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [DealVolume] (
   [DealId] [bigint] NOT NULL ,
   [Point] [varchar] (40) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
   [FromDate] [datetime] NOT NULL ,
   [ToDate] [datetime] NOT NULL ,
   [Quantity] [numeric](18, 0) NULL ,
   [UOMId] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
   CONSTRAINT [PK_DealVolume] PRIMARY KEY  CLUSTERED
   (
      [DealId],
      [Point],
      [FromDate]
   ) WITH  FILLFACTOR = 90  ON [PRIMARY] ,
   CONSTRAINT [FK_DealVolume_Deal] FOREIGN KEY
   (
      [DealId]
   ) REFERENCES [Deal] (
      [DealId]
   ) NOT FOR REPLICATION
) ON [PRIMARY]
GO


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 6:26 pm 
Newbie

Joined: Tue Oct 11, 2005 3:18 pm
Posts: 16
To make my example an independent test case, or at least independent of any other objects (other than Deal and DealVolume), just comment out the properties referencing Portfolio, Counterparty, and LegalEntity.

Please let me know if there is anything else you need to recreate this. Thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 06, 2005 11:08 am 
Contributor
Contributor

Joined: Thu May 12, 2005 9:45 am
Posts: 593
Location: nhibernate.org
The last think missing is the TestCase :wink:
A class doing successivily your tests 1, 2 and 3.

_________________
Pierre Henri Kuaté.
Get NHibernate in Action Now!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Nov 07, 2005 12:24 pm 
Newbie

Joined: Tue Oct 11, 2005 3:18 pm
Posts: 16
Test 3 would simply create a configuration, get a session, run the first hql, get another session, run the second hql.... like this...

Code:
NHibernate.Cfg.Configuration configuration;
NHibernate.ISessionFactory sessionFactory;
NHibernate.ISession session;
NHibernate.IQuery q;
string hql;
IList ilist;
ArrayList al;
Deal testdeal,testdeal2;

configuration = new NHibernate.Cfg.Configuration();
configuration.AddAssembly("BP.Viper.DataObjects");
sessionFactory = configuration.BuildSessionFactory();
session = sessionFactory.OpenSession();
hql = "from Deal d join fetch d.Volumes v where d.Dealid = (select max(i.Dealid) from DealVolume i)";
q = session.CreateQuery(hql);
ilist = q.List();
session.Close();
al = new ArrayList(ilist);
testdeal = (Deal)al[0];

session = sessionFactory.OpenSession();
hql = "from Deal d join fetch d.Volumes v where d.Dealid = (select max(i.Dealid) from DealVolume i where i.Dealid < "+testdeal.Dealid+")";
q = session.CreateQuery(hql);
ilist = q.List();
session.Close();
al = new ArrayList(ilist);
testdeal2 = (Deal)al[0];


Then with your debugger, just look inside testdeal2, look inside his bag of volumes, and look at the parent Deal objects... they will indicate the wrong parent Dealid. An NUnit test would look like this...

Code:
Assert.AreEqual( testdeal2.Dealid, ((DealVolume)testdeal2.Volumes[0]).Deal.Dealid );


You can run tests 1 and 2 by simply commenting one hql or the other, so that only one runs. Those two tests are just to prove that the hql is good and will produce correct results when run in isolation.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 16, 2005 2:04 pm 
Newbie

Joined: Tue Oct 11, 2005 2:44 pm
Posts: 12
I think you need to override the ToString() as well, since it is going to be used as the cache key. If you don't override it, then the class name is used as the key


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 06, 2005 11:39 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
The bug is fixed now, but note that the object in question has an invalid implementation of GetHashCode.


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