-->
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.  [ 1 post ] 
Author Message
 Post subject: NormalizedEntityPersister: returns same Discriminator value
PostPosted: Wed Jun 22, 2005 5:25 pm 
Newbie

Joined: Wed Jun 22, 2005 4:20 pm
Posts: 3
Location: San Jose, CA - USA
I am using the "table-per-subclass" strategy to map a hierarchy based on a Payment base class. The children classes would be, for instance, CreditCardPayment, CheckPayment, etc.

The class code and mapping files are in the bottom of this post. The mapping is working fine and data retrieval/persistence is working as expected.

However, I am running into problems when performing polymorphic queries filtered by a specific type of class, by using the ".class" property. Say, for example, I would like to have a query that would select only payments of type "CheckPayment" (obs: I know that's a dumb example, I could just do "from CheckPayment" instead; but just for the sake of having a simple example here...).

Code:
select P from Payment as P where P.class = CheckPayment


The problem is that the .NET class names embedded in the where clause ("CheckPayment") are always translated to base class ("Payment") discriminator value (which is 0).

Here is the translated SQL query (subclasses attributes were ommitted to make it shorter). See the (incorrectly translated) "0" value in bold

select payment0_.ID as ID,
case when [payment0__1_].Id is not null then 1
when [payment0__2_].Id is not null then 2
when payment0_.ID is not null then 0 end
as clazz_,
from Payment payment0_
left outer join CheckPayment [payment0__1_]
on payment0_.ID=[payment0__1_].Id
left outer join CreditCardPayment [payment0__2_]
on payment0_.ID=[payment0__2_].Id
where
(case when [payment0__1_].Id is not null then 1
when [payment0__2_].Id is not null then 2
when payment0_.ID is not null then 0
end=0)


I tracked the problem in the source code and found that the NormalizedEntityPersister class (which is the ClassPersister used by "joined-subclass" hierarchies) always returns the same Discriminator value ("0"), regardless of the associated persisting class.

Here is the piece of the code I am referring to:

Code:
// NormalizedEntityPersister constructor

public NormalizedEntityPersister( PersistentClass model, ISessionFactoryImplementor factory )
: base( model, factory )
{
    /// Some code before... ///

    try
    {
        this.discriminatorType = ( IDiscriminatorType )NHibernateUtil.Int32;
        discriminatorValue = 0;
        // That's the only line that assigns a value to discriminatorSQLString!!
        this.discriminatorSQLString = "0";
    }
    catch( Exception e )
    {
        throw new MappingException( "Could not format discriminator value '0' to sql string using the IType NHibernate.Types.Int32Type", e );
    }
    /// Some code after... ///
}

public override object DiscriminatorSQLValue
{
    // Always return 0 !!!
    get { return discriminatorSQLString; }
}


I wonder whether this is the current expected behavior (so, we should not try to use the ".class" property in HQL queries with classes that use "per-subclass" strategy) or a fixable bug.

In fact, even though <joined-subclass> mappings do not define a explicit discriminator value for each subclass, the NormalizedEntityPersister instance for the base class (which would be "Payment" in this case) does generate a set of discriminator values for each mapped subclass (a sequence of integer numbers, starting from 0, reserved for the base class), which is available through the GetSubclassForDiscriminatorValue method of the NormalizedEntityPersister of the base class of the hierarchy ("Payment", in this example).

So theorically, the code should be able to assign the correct discriminator value for each instance of NormalizedEntityPersister.

Anyway, that's just a suggestion. The most important factor would be the design aspect. If this is a design decision, that would be OK for me. In this case, I would just need a confirmation for that.

Here is the mapping file and the classes code. To make it shorter, I will just show 2 Payment subclasses (CreditCard and Check):

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" assembly = "MyTest" namespace = "MyTest">

    <class name="Payment" table="Payment" proxy = "Payment">
        <id name="Id" type="Int64" unsaved-value="0" column="ID">
            <generator class="native"/>
        </id>
        <property name="Amount" column="Amount"/>
    </class>

    <joined-subclass name="CheckPayment" table="CheckPayment" proxy="CheckPayment" extends="Payment">
        <key column="Id"/>
        <property name="Bank"/>
        <property name="CheckNumber"/>
    </joined-subclass>
      
    <joined-subclass name="CreditCardPayment" table="CreditCardPayment" proxy="CreditCardPayment" extends="Payment">
        <key column="Id"/>
        <property name="CreditCardNumber"/>
   </joined-subclass>

</hibernate-mapping>


Code:
public abstract class Payment
{
     private decimal _amount;
     private long _id = 0;

     public Payment(){}

     protected virtual long Id
     {
        get { return _id; }
        set { _id = value; }
     }

    public virtual decimal Amount
    {
       get { return _amount; }
       set { _amount = value; }
    }
}


Code:
public class CreditCardPayment : Payment
{
    private string _number;

    public virtual string CreditCardNumber
    {
        get { return _number; }
        set { _number = value; }
    }
}


Code:
public class CheckPayment : Payment
{
    private string _bank;
    private string _number;

    public virtual string Bank
    {
        get { return _bank; }
        set { _bank = value; }
    }

    public virtual string CheckNumber
    {
        get { return _number; }
        set { _number = value; }
    }
}


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

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.