-->
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: Mapping two classes to the same table
PostPosted: Fri Jun 23, 2006 11:56 am 
Newbie

Joined: Fri Jun 23, 2006 9:56 am
Posts: 10
Hi all,

I am finding it difficult to map two different classes (ClassA and ClassB) to different columns in the same table. Both classes share the same composite id, as shown below.

Code:
public class CompositeId {
   public long compositeLong;
   public String compositeString;
}

public abstract class BaseClass {
   public CompositeId id;
}

public abstract class ClassA extends BaseClass {
   public String propertyA;
}

public class ClassABlack extends ClassA {
   public String propertyAtype1;
}

public class ClassAWhite extends ClassA {
   public String propertyAtype2;
}

public abstract class ClassB extends BaseClass {
   public String propertyB;
}

public class ClassBBlack extends ClassB {
   public String propertyBtype1;
}

public class ClassBWhite extends ClassB {
   public String propertyBtype2;
}


Table structure:

Code:
CREATE TABLE base_class (
    composite_long          int      NOT NULL,
    composite_string        char(8)  NOT NULL,
    a_isblack               char(1)  NULL,
    a_type1                 char(8)  NULL,
    a_type2                 char(8)  NULL,
    b_isblack               char(1)  NULL,
    b_type1                 char(8)  NULL,
    b_type2                 char(8)  NULL,
) ...


These two abstract classes, in turn, have to subclasses each, with a different discriminator column. It is possible to create and save an instance for any of the classes (say ClassAWhite). However, when creating and instance of any of the subclasses of ClassB, the discriminator column for this is not set correctly, being left as null. As an example, when trying to save an instance of ClassBBlack, the following SQL is generated:

Code:
update base_class set b_type1=? where composite_long=? and composite_string=?


This should be:

Code:
update base_class set b_isblack=?, b_type1=? where composite_long=? and composite_string=?


Any hints?

Hibernate version: 3.2.1

Mapping documents:

Code:
<hibernate-mapping>
    <class name="ClassA" table="base_class" abstract="true">
        <composite-id name="id" class="CompositeId">
            <key-property name="compositeLong" column="composite_long" type="long" />
            <key-property name="compositeString" column="composite_string" />
        </composite-id>

        <discriminator column="a_isblack" not-null="true" type="string" />

        <subclass name="ClassABlack" discriminator-value="Y">
            <property name="propertyAtype1" column="a_type1" />
        </subclass>

        <subclass name="ClassAWhite" discriminator-value="N">
            <property name="propertyAtype2" column="a_type2" />
        </subclass>
    </class>

    <class name="ClassB" table="base_class" abstract="true">
        <composite-id name="id" class="CompositeId">
            <key-property name="compositeLong" column="composite_long" type="long" />
            <key-property name="compositeString" column="composite_string" />
        </composite-id>

        <discriminator column="b_isblack" not-null="true" type="string" />

        <subclass name="ClassBBlack" discriminator-value="Y">
            <property name="propertyBtype1" column="b_type1" />
        </subclass>

        <subclass name="ClassBWhite" discriminator-value="N">
            <property name="propertyBtype2" column="b_type2" />
        </subclass>
    </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close(): N/A

Full stack trace of any exception that occurs: N/A

Name and version of the database you are using: Oracle 9.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 25, 2006 2:46 pm 
Expert
Expert

Joined: Tue Dec 28, 2004 7:02 am
Posts: 573
Location: Toulouse, France
If I followed correctly, you've got 5 classes :

Code:
                       BaseClass
                       /           \
                  ClassA         ClassB
                /        \           /\
      ClassAWhite   ClassABlack      ... ...

You only mapped BaseClass, ClassA and ClassB. And you're trying to save instances of say ClassAWhite without having mapped it, considering the super-class mapping (ClassA, in this case) should be taken ?

If that's it, I don't think Hibernate can handle this case directly. But if you're ready to do tricky things, I'd point you to the Interceptor class. Maybe you should be able this way to specify the mapped class (entity) you want to be used.

Let me know what you think or have tried :-)

_________________
Baptiste
PS : please don't forget to give credits below if you found this answer useful :)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 3:13 am 
Newbie

Joined: Fri Jun 23, 2006 9:56 am
Posts: 10
ClassAWhite, ClassBWhite, ClassABlack and ClassBBlack are mapped as subclasses, as shown in the mapping metadata below. As I see it, the problem seems to be that Hibernate only sets discriminator columns on insert and not on update. Hence, one of the discriminator columns in table base_class is always left as null.

Quote:
<hibernate-mapping>
<class name="ClassA" table="base_class" abstract="true">
<composite-id name="id" class="CompositeId">
<key-property name="compositeLong" column="composite_long" type="long" />
<key-property name="compositeString" column="composite_string" />
</composite-id>

<discriminator column="a_isblack" not-null="true" type="string" />

<subclass name="ClassABlack" discriminator-value="Y">
<property name="propertyAtype1" column="a_type1" />
</subclass>

<subclass name="ClassAWhite" discriminator-value="N">
<property name="propertyAtype2" column="a_type2" />
</subclass>

</class>

<class name="ClassB" table="base_class" abstract="true">
<composite-id name="id" class="CompositeId">
<key-property name="compositeLong" column="composite_long" type="long" />
<key-property name="compositeString" column="composite_string" />
</composite-id>

<discriminator column="b_isblack" not-null="true" type="string" />

<subclass name="ClassBBlack" discriminator-value="Y">
<property name="propertyBtype1" column="b_type1" />
</subclass>

<subclass name="ClassBWhite" discriminator-value="N">
<property name="propertyBtype2" column="b_type2" />
</subclass>

</class>
</hibernate-mapping>
[/b]


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 4:46 am 
Expert
Expert

Joined: Tue Dec 28, 2004 7:02 am
Posts: 573
Location: Toulouse, France
I can't find it in the forum, but I recently read a post about someone wanting to change class type of an object.

If that's what you want to do, it's impossible. You must use a SQL Query to do update the discriminator column manually.

If you find this subject, please link it from here. I think people would be happy to find information about it.

The reason for this is that in fact, in Java it's totally impossible to change the type of a variable without creating a new instance. So that's why the update in the db must be done manually.

_________________
Baptiste
PS : please don't forget to give credits below if you found this answer useful :)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 5:06 am 
Newbie

Joined: Fri Jun 23, 2006 9:56 am
Posts: 10
batman,

Many thanks for your response.

Do you mean this post?
http://forum.hibernate.org/viewtopic.php?t=954526

Please note that my problem, though similar, is not exactly the same. I do not pretend to change the class of an instace at runtime. I just need to map two abstract classes to different columns in the same table. These two abstract classes have, in turn, two concrete subclasses each. This leads to two discriminator columns in the table. Hibernate will set the value for one of these discriminators but will not update the value of the other one on updates to the same table record.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 7:34 am 
Newbie

Joined: Fri Jun 23, 2006 9:56 am
Posts: 10
I have found a workaround to the save/update problem by using formulas in discriminators and property in the two abstract classes, as shown below. However, the problem now is that Hibernate drops the whole database record when deleting an object for which a subclass of the other abstract class exists.

Is there a way to make deletes set the columns to NULL when another entity exists in the same table row? Or an easier way to map two different classes to the same table row?

Code:
<hibernate-mapping>
    <class name="ClassA" table="base_class" abstract="true">
        <composite-id name="id" class="CompositeId">
            <key-property name="compositeLong" column="composite_long" type="long" />
            <key-property name="compositeString" column="composite_string" />
        </composite-id>

        <discriminator formula="a_isblack" type="string" />

        <property name="black" column="a_isblack" type="yes_no" />

        <subclass name="ClassABlack" discriminator-value="Y">
            <property name="propertyAtype1" column="a_type1" />
        </subclass>

        <subclass name="ClassAWhite" discriminator-value="N">
            <property name="propertyAtype2" column="a_type2" />
        </subclass>
    </class>

    <class name="ClassB" table="base_class" abstract="true">
        <composite-id name="id" class="CompositeId">
            <key-property name="compositeLong" column="composite_long" type="long" />
            <key-property name="compositeString" column="composite_string" />
        </composite-id>

        <discriminator formula="b_isblack" type="string" />

        <property name="black" column="a_isblack" type="yes_no" />

        <subclass name="ClassBBlack" discriminator-value="Y">
            <property name="propertyBtype1" column="b_type1" />
        </subclass>

        <subclass name="ClassBWhite" discriminator-value="N">
            <property name="propertyBtype2" column="b_type2" />
        </subclass>
    </class>
</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 12:55 pm 
Regular
Regular

Joined: Tue Mar 21, 2006 11:01 am
Posts: 65
I may be misunderstanding you but I don't see any mapping to BaseClass in your mapping file. I think what you want is something like this:

Code:
<class name="BaseClass" table="base_class">
    <subclass name="ClassA" ... >
         <subclass name="ClassABlack" discriminator="AY" ... />
         <subclass name="ClassWhite" discriminator="AN" ... />
    </subclass>

    <subclass name="ClassB" ... >
         <subclass name="ClassBBlack" discriminator="BY" ... />
         <subclass name="ClassBWhite" discriminator="BN" ... />
    </subclass>

</class>


Subclasses are nestable.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 1:09 pm 
Newbie

Joined: Fri Jun 23, 2006 9:56 am
Posts: 10
That would work. However, I see two problems:

- ClassA and ClassB have different discriminator columns. Thus, we need two <class> elements in the mapping file.

- For each instance of ClassA, there can be an instance of ClassB with the same composite PK (and hence identifier). For legacy reasons, at the database level this is represented by both classes being persisted in different columns in the same table. If I map both instances as you propose, Hibernate will pressumably complain if it detects two such entities with the same identifier.

Ideas?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 1:48 pm 
Regular
Regular

Joined: Tue Mar 21, 2006 11:01 am
Posts: 65
I see. I didn't read carefully enough and wasn't aware that you were tied to a legacy schema. And only <class> elements can have <discriminator>, <subclass> cannot take a <discriminator>.

However, this statement now jumps out at me:

Quote:
For each instance of ClassA, there can be an instance of ClassB with the same composite PK (and hence identifier)


How can that be? You can't have a non-unique primary key, but you're telling me that you do. Shouldn't the discriminator between A and B be part of your composite primary key?

No, wait, I see or think I do. You want two different entities to share same table row? That sounds like asking for trouble. Why is that required, and what are you trying to accomplish with it? Would these two entities ever exist at the same time? Are you sure your inheritance tree accurately describes what you're trying to achieve?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 27, 2006 3:23 am 
Newbie

Joined: Fri Jun 23, 2006 9:56 am
Posts: 10
Quote:
No, wait, I see or think I do. You want two different entities to share same table row? That sounds like asking for trouble. Why is that required, and what are you trying to accomplish with it? Would these two entities ever exist at the same time? Are you sure your inheritance tree accurately describes what you're trying to achieve?


That's right. What I'm trying to represent is a one-to-one relationship between a third class (MainClass) and each of ClassA and ClassB. The composite PK of BaseClass is in fact a FK to MainClass. That's why ClassA and ClassB have the same composite identifier. When there is no ClassA for a given MainClass but there is a ClassB, the discriminator column corresponding to ClassA is left as null in the database, while the discriminator for ClassB is not-null in the same table row. Uniqueness of the composite PK is therefore ensured.

I know this scenario is not directly contemplated in Hibernate. However, I've made it work with saves and updates. Still having problems on deletes, which drop the whole row even if there is a second entity persisted there.

I guess I will have to implement a custom EntityPersister or something closer to Hibernate internal's. As I see, there is no way to get this to work with standard metadata.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 27, 2006 2:14 pm 
Regular
Regular

Joined: Tue Mar 21, 2006 11:01 am
Posts: 65
Quote:
I guess I will have to implement a custom EntityPersister or something closer to Hibernate internal's. As I see, there is no way to get this to work with standard metadata.


Yes, I think you're probably right. You're definitely outside of the "standard model" here.

And while I can't know for sure with the level of detail you've provided, so take this for what it's worth - maybe nothing - but it seems to me that you might want to ask yourself whether it's really true that ClassA IS-A MainClass and whether it wouldn't be more productive to try viewing it as ClassA HAS-A MainClass (or vice versa). This doesn't sound like classical inheritance to me, but as I say, I lack details.


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:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.