-->
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.  [ 14 posts ] 
Author Message
 Post subject: Mapping common properties of classes in a common file
PostPosted: Thu Jun 08, 2006 12:19 pm 
Beginner
Beginner

Joined: Fri Feb 11, 2005 2:40 pm
Posts: 27
We are using table-per-concrete-class mapping strategy for inheritance. All our tables have ID, VERSION, CREATED_DATE and UPDATED_DATE columns. These properties are defined in a common base class extended by all mapped classes. The problem is that every .hbm file has to define the mapping for these 4 fields.

We can not use union-subclass style mapping becuase it seems to force you to define all subclasses in the same file, and we have too many classes.

When I tried using ENTITY to define something like &commonProperties, it would work as long as the Hibernate configuration is loaded as a file. When we try loading it as a resource from class path, the entity resolution fails.

This should be a rather common problem because every project I've been on has to make that decision. In my mind, the ideal solution would be to have mapping of the abstract base class to be used as defaults, allowing the subclasses to override those properties if the column names are different. But I don't think that's how Hibernate works.

Any suggestions would be welcome.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 08, 2006 11:32 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
union-subclass doesn't require that the abstract superclass is defined in the same file. So long as the mapping file containing the superclass is loaded by the session factory (e.g. is in the .cfg.xml file's <session-factory><mapping> element), and the references from the concrete suibclasses' mappings uses the full package name to refer to the superclass, it should work.

When using union-subclass in this way, you have to provide the extends="" attribute.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 09, 2006 4:05 am 
Regular
Regular

Joined: Fri Oct 01, 2004 2:19 am
Posts: 111
Location: Melbourne, Australia
I have a similar setup in my project and I resorted to using XML
entity definition and references like so:



Code:
<!DOCTYPE hibernate-mapping SYSTEM
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
    [<!ENTITY netobj-attrs-incl SYSTEM "netobj-attrs-incl.hbm.xml">]>

<hibernate-mapping package="company.model.beans">

    <!-- Some class Hierarchy -->
    <class name="SomeClass" table="SOME_CLASS" discriminator-value="SC">

        <!-- include base attributes -->
        &netobj-attrs-incl;

       <!-- other attributes per the class -->

    <subclass name="SubClass" extends="SomeClass" discriminator-value="SUBC"/>

</class>



And this is repeated for each sub hierarchy.

Hope this helps.

_________________
Cheers,

Bonny

please don't forget to rate :)


Top
 Profile  
 
 Post subject: It worked
PostPosted: Fri Jun 09, 2006 11:17 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 2:40 pm
Posts: 27
Thanks both of you guys. I've implemented union-subclass as suggested by tenwit and it worked perfectly. Once again I'm in love with Hibernate:-)

bonnyr, I've tried using ENTITY in the past and like I said, it wasn't working once I attempted to load the hibernate configuration files from the classpath. I use Spring to load and configure Hibernate session factory. Have you tried using your approach from a .ear or .war?


Top
 Profile  
 
 Post subject: Re: It worked
PostPosted: Fri Jun 09, 2006 8:00 pm 
Regular
Regular

Joined: Fri Oct 01, 2004 2:19 am
Posts: 111
Location: Melbourne, Australia
xplorem wrote:
Thanks both of you guys. I've implemented union-subclass as suggested by tenwit and it worked perfectly. Once again I'm in love with Hibernate:-)

bonnyr, I've tried using ENTITY in the past and like I said, it wasn't working once I attempted to load the hibernate configuration files from the classpath. I use Spring to load and configure Hibernate session factory. Have you tried using your approach from a .ear or .war?


Our Hibernate files are inside one of the jars being used for the application
(not your typical j2ee app), but we faced the same problem and the
solution (for us at least), was to provide an implementation of
EntityResolver when building the factory. This is being called to resolve
the entities and since we are in control of where those mapping files are.
we could very easily load these entities.

HTH

_________________
Cheers,

Bonny

please don't forget to rate :)


Top
 Profile  
 
 Post subject: Problems with loading assotiation - a bug?
PostPosted: Mon Jun 12, 2006 4:55 pm 
Beginner
Beginner

Joined: Fri Feb 11, 2005 2:40 pm
Posts: 27
I have switched to <union-subclass> mapping which was exactly what I was looking for. However, I ran into issues when running the application against the database, because of undesirable polymorphic behavior. We have 2 tables - LENDER and LOAN. Both have ID as a primary key, and LOAN has LENDER_ID as a foreign key into LENDER to model many to one. In Java there are 2 classes, Lender and Loan that extend AbstractModelObject, which contains all common attributes. Loan has a property of type Lender with getter and setter. I've pasted the mapping for Loan and AbstractModelObject below.

In the database I have 1 row in LENDER table with ID=1, and 2 rows in LOAN table with IDs 1 and 2. Both loans have their foreign key set to "1" pointing to the lender. When I tried running the test method that would load a loan, it failed with the exception saying that the loaded object was not of specified class. Eventually I realized that what Hibernate was doing was trying to use the instance of the Loan with id of 1 instead of loading an instance of Lender with id of 1. In other words, it seems that with that mapping one can not have two entities with the same ID derived from the same superclass. If you have many tables, all IDs in them will have to be sequential which is rather limiting.

Is there some setting I can change to have Hibernate use the correct type? It sounds like a bug in Hibernate to me...

Exception
org.hibernate.WrongClassException: Object with id: 1 was not of the specified subclass: com.fanniemae.lacrs.examples.simplecrud.model.Loan (loaded object was of wrong class)


Loan Mapping
<hibernate-mapping>
<union-subclass name="com.fanniemae.lacrs.examples.simplecrud.model.Loan" table="loan" extends="com.fanniemae.lacrs.core.model.AbstractModelObject">
<!-- Properties -->
<property name="fannieMaeLoanNumber" type="string">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="fannie_mae_loan_number" length="50"/>
</property>
<property name="upb" type="big_decimal">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="upb" precision="10" scale="7"/>
</property>
<property name="interestRate" type="big_decimal">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="interest_rate" precision="10" scale="7"/>
</property>

<!-- Associations -->
<many-to-one name="lender" class="com.fanniemae.lacrs.examples.simplecrud.model.Lender" >
<column name="Lender_id" not-null="true"/>
</many-to-one>
</union-subclass>
</hibernate-mapping>


Abstract Model Object Mapping
<hibernate-mapping>

<class name="com.fanniemae.lacrs.core.model.AbstractModelObject" abstract="true">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>

<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="assigned" />
</id>
<version name="version" type="long">
<column name="version"/>
</version>
<property name="createDate" type="date">
<column name="create_date" length="10" not-null="true"/>
</property>
<property name="createdBy" type="string">
<column name="created_by" not-null="true"/>
</property>
</class>
</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 12, 2006 9:17 pm 
Regular
Regular

Joined: Fri Oct 01, 2004 2:19 am
Posts: 111
Location: Melbourne, Australia
From memory, this was one of the problems I faced when I started
mapping using union-subclass, but I may be wrong since our setup
is slightly different - I also need to be able to have a
polymorphic reference to the root class at the database level (i.e.
keep a hetrogeneous collection of objects whose only common
attributes are those of the base class) and I think the only way to achieve
this (using <any>) could not be done using union-subclass.

The entity inclusion approach means that I can control the base class
attribute with relative ease, and Hibernate does not care that the classes,
in the JVM, are all derived from a base class and I can use the <any>
mapping quite easily.

_________________
Cheers,

Bonny

please don't forget to rate :)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 12, 2006 9:30 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Can't you do that by specifying the entity-name attribute on each class with a common base class, and using the entity name where necessary? Have a read of section 5.3. "Mapping a class more than once" of the ref docs: I think that that can be adapted to solve this problem.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject: entity-name didn't help
PostPosted: Tue Jun 13, 2006 11:43 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 2:40 pm
Posts: 27
tenwit wrote:
Can't you do that by specifying the entity-name attribute on each class with a common base class, and using the entity name where necessary? Have a read of section 5.3. "Mapping a class more than once" of the ref docs: I think that that can be adapted to solve this problem.


I read the doc and added "entity-name" attribute to the mapping (see below). It didn't help with the problem, I got the same WrongClassException because it tries to assign Loan to setLender on a Loan. The updated mapping for Loan is below.

Honestly, even if it did work with entity-name I feel we shouldn't have to put it. According to documentation one is supposed to use it when mapping same class more then once. I only map Loan and Lender once. I understand that at the superclass level it wouldn't make sense to have 2 instances with the same ID, even of a different subclass. But on the subclass level it shouldn't try to use Loan with id of 1 when looking for a Lender with id of 1.

So it seems that going the ENTITY route is the only option. While I'm sure it will work, it seems like a hack. Seems to me that either Hibernate should support some kind of "<include file=""/> construct that can be used to include common properties, or a check should be added to not grab the wrong class.

Any more ideas, anybody?

<hibernate-mapping>

<union-subclass name="com.fanniemae.lacrs.examples.simplecrud.model.Lender" entity-name="com.fanniemae.lacrs.examples.simplecrud.model.Lender" table="lender" extends="com.fanniemae.lacrs.core.model.AbstractModelObject">

<!-- Properties -->
<property name="name" type="string">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="name" not-null="true"/>
</property>

<!-- Associations -->
<set name="loans" inverse="true">
<key>
<column name="Lender_id" not-null="true"/>
</key>
<one-to-many class="com.fanniemae.lacrs.examples.simplecrud.model.Loan" />
</set>
</union-subclass>
</hibernate-mapping>

Loan mapping is pretty much the same.


Top
 Profile  
 
 Post subject: Loan mapping with entity-name
PostPosted: Tue Jun 13, 2006 11:45 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 2:40 pm
Posts: 27
Just in case, here's the Loan mapping with entity-name:

<!-- Generated Jun 1, 2006 4:31:01 PM by Hibernate Tools 3.1.0.beta5 -->
<hibernate-mapping>
<union-subclass name="com.fanniemae.lacrs.examples.simplecrud.model.Loan" entity-name="com.fanniemae.lacrs.examples.simplecrud.model.Loan" table="loan" extends="com.fanniemae.lacrs.core.model.AbstractModelObject">
<!-- Properties -->
<property name="fannieMaeLoanNumber" type="string">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="fannie_mae_loan_number" length="50"/>
</property>
<property name="upb" type="big_decimal">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="upb" precision="10" scale="7"/>
</property>
<property name="interestRate" type="big_decimal">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="interest_rate" precision="10" scale="7"/>
</property>

<!-- Associations -->
<!--many-to-one name="lender" class="com.fanniemae.lacrs.examples.simplecrud.model.Lender" -->
<many-to-one name="lender" entity-name="com.fanniemae.lacrs.examples.simplecrud.model.Lender" >
<column name="Lender_id" not-null="true"/>
</many-to-one>
</union-subclass>
</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 4:59 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
You're not making enough use of the entity name. This is the process (roughly):
  1. Set up the entity-name attribute on both Loan and Lender classes.
  2. In all collections and associations referring to either of those class and where you want to be specific about which one you're referring to, do not use the class="" attribute. Use the entity-name="" attribute instead.

So the loans set in Lender needs to change, too.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 5:52 pm 
Beginner
Beginner

Joined: Fri Feb 11, 2005 2:40 pm
Posts: 27
tenwit wrote:
You're not making enough use of the entity name. This is the process (roughly):
  1. Set up the entity-name attribute on both Loan and Lender classes.
  2. In all collections and associations referring to either of those class and where you want to be specific about which one you're referring to, do not use the class="" attribute. Use the entity-name="" attribute instead.
So the loans set in Lender needs to change, too.


Actually, if you look at my mapping I was referring to Lender from a Loan by entity-name only. It's just that my entity names are the same as class names. I've changed it to be simpler and updated the association from Lender to Laon just in case, but the problem is still there. Here are the mappings:

<hibernate-mapping>
<union-subclass name="com.fanniemae.lacrs.examples.simplecrud.model.Loan" entity-name="Loan" table="loan" extends="com.fanniemae.lacrs.core.model.AbstractModelObject">
<!-- Properties -->
<property name="fannieMaeLoanNumber" type="string">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="fannie_mae_loan_number" length="50"/>
</property>
<property name="upb" type="big_decimal">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="upb" precision="10" scale="7"/>
</property>
<property name="interestRate" type="big_decimal">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="interest_rate" precision="10" scale="7"/>
</property>

<!-- Associations -->
<!--many-to-one name="lender" class="com.fanniemae.lacrs.examples.simplecrud.model.Lender" -->
<many-to-one name="lender" entity-name="Lender" >
<column name="Lender_id" not-null="true"/>
</many-to-one>
</union-subclass>
</hibernate-mapping>

and Lender

<union-subclass name="com.fanniemae.lacrs.examples.simplecrud.model.Lender" entity-name="Lender" table="lender" extends="com.fanniemae.lacrs.core.model.AbstractModelObject">

<!-- Properties -->
<property name="name" type="string">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="name" not-null="true"/>
</property>

<!-- Associations -->
<set name="loans" inverse="true">
<key>
<column name="Lender_id" not-null="true"/>
</key>
<one-to-many entity-name="Loan" />
</set>
</union-subclass>

So are you sure that it is supposed to work?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 7:42 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
It was the association from Lender to Load that I was talking about, so your later update fixed that.

I would have thought that it would work with what you've got now.

Given that it's not working for you, your options now include:

- Going back to listing the duplicated properties, and not using union-subclass.
- Switching to joined-subclass.
- Using delegation in you java class, and mapping your base class and subclasses separately. This will eliminate the repetitive mappings, but only by moving the repition to java, where it's probably even worse (seeing as you need setters, getters and a nested object to implement delegation).

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject: Issue closed
PostPosted: Wed Jun 14, 2006 10:37 am 
Beginner
Beginner

Joined: Fri Feb 11, 2005 2:40 pm
Posts: 27
tenwit wrote:
It was the association from Lender to Load that I was talking about, so your later update fixed that.

I would have thought that it would work with what you've got now.

Given that it's not working for you, your options now include:

- Going back to listing the duplicated properties, and not using union-subclass.
- Switching to joined-subclass.
- Using delegation in you java class, and mapping your base class and subclasses separately. This will eliminate the repetitive mappings, but only by moving the repition to java, where it's probably even worse (seeing as you need setters, getters and a nested object to implement delegation).


tenwit, thanks for suggestions again. I think the best approach would be using ENTITY to include the common definitions, as suggested by bonnyr. That way the IDs do not have to be unique between the classes but there is no duplication. I was happy to discover that the latest version of Hibernate (3.1.3) includes an entity resolver that will for files on the classpath. This means that I don't even have to write my own entity resolver and pass it to the configuration (which was somewhat tricky with Spring). Here's my final mapping for Lender:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
<!ENTITY AbstractModelObject-properties SYSTEM "file://persist/hibernate/classes/AbstractModelObject-properties.hbm.xml" >
]>

<hibernate-mapping>

<!--union-subclass name="com.fanniemae.lacrs.examples.simplecrud.model.Lender" entity-name="Lender" table="lender" extends="com.fanniemae.lacrs.core.model.AbstractModelObject"-->
<class name="com.fanniemae.lacrs.examples.simplecrud.model.Lender" entity-name="Lender" table="lender" >

<!-- Include superclass properties -->
&AbstractModelObject-properties;

<!-- Properties -->
<property name="name" type="string">
<meta attribute="use-in-tostring">true</meta>
<meta attribute="use-in-equals">true</meta>
<column name="name" not-null="true"/>
</property>

<!-- Associations -->
<set name="loans" inverse="true">
<key>
<column name="Lender_id" not-null="true"/>
</key>
<one-to-many class="com.fanniemae.lacrs.examples.simplecrud.model.Loan" />
</set>
</class>
</hibernate-mapping>

I would consider the issue closed. Thanks for your help guys again.


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