-->
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.  [ 5 posts ] 
Author Message
 Post subject: Hibernate (Annotations) inheritance mapping woes
PostPosted: Tue Mar 17, 2009 6:09 am 
Newbie

Joined: Tue Mar 17, 2009 3:27 am
Posts: 3
Hibernate version: 3.3.1-ga
Hibernate Annotations version: 3.4.0-ga
Mapping documents: Hibernate Annotations
Name and version of the database you are using: Oracle 9i
Application Server: Glassfish v3 Prelude

First, I'd like to apologize for the ambiguous subject, but I'm incapable of describing it any better. Here's the situation:

I'm trying to model a legacy database with Hibernate (Annotations). The database structure for the relevant parts is like this:

Image

ITEMINFOTABLE's itemid is an assiociation to ITEMTABLE and moduletype has three possible values: 0, 1, 2 which denote different info modules.

My approach is to have one base class, of which I inherit the appropriate classes. Here's code for each class:

WARNING! I've edited names etc. a bit in order to hide information that might reveal things my employer might want to keep a secret.

Item.java
Code:
package my.package.model;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.Where;

/**
*
* @author por
*/
@Entity
@Table( name = "ITEMTABLE" )
@Where( clause = "MYCONDITION" )
public class Item implements Serializable
{
    private String              id;
    private String              name;
    private ItemType          type;
    private String              groupId;
    // private ItemInfoPurchase  purchaseInfo;
    // private ItemInfoInventory inventoryInfo;
    // private ItemInfoSales     salesInfo;
   
    public enum ItemType {
        EnumValue1,
        EnumValue2,
        EnumValue3,
    };

    @Id
    @Type( type = "LegacyDBString" )
    @Column( length = 30, name = "ITEMID" )
    public String getId()
    {
        return id;
    }

    public void setId( String id )
    {
        this.id = id;
    }

    @Type( type = "LegacyDBString" )
    @Column( length = 255, name = "ITEMNAME" )
    public String getName()
    {
        return name;
    }

    public void setName( String name )
    {
        this.name = name;
    }

    @Column( name = "ITEMTYPE" )
    public ItemType getType()
    {
        return type;
    }

    public void setType( ItemType type )
    {
        this.type = type;
    }

    @Type( type = "LegacyDBString" )
    @Column( name = "ITEMGROUPID" )
    public String getGroupId()
    {
        return groupId;
    }

    public void setGroupId( String groupId )
    {
        this.groupId = groupId;
    }

    /*
     * This is a failed attempt to associate ItemInfo to Item class

    @OneToOne( optional = false )
    @JoinColumn( name = "ITEMID", referencedColumnName = "ITEMID" )
    @Where( clause = "MYCONDITION")
    public ItemInfoSales getSalesInfo()
    {
        return ( salesInfo );
    }

    public void setSalesInfo( ItemInfoSales salesInfo )
    {
        this.salesInfo = salesInfo;
    }

    @OneToOne( optional = false )
    @JoinColumn( name = "ITEMID", referencedColumnName = "ITEMID" )
    @Where( clause = "MYCONDITION")
    public ItemInfoPurchase getPurchaseInfo()
    {
        return ( purchaseInfo );
    }

    public void setPurchaseInfo( ItemInfoPurchase purchaseInfo )
    {
        this.purchaseInfo = purchaseInfo;
    }

    @OneToOne( optional = false )
    @JoinColumn( name = "ITEMID", referencedColumnName = "ITEMID" )
    @Where( clause = "MYCONDITION")
    public ItemInfoInventory getInventoryInfo()
    {
        return ( this.inventoryInfo );
    }

    public void setInventoryInfo( ItemInfoInventory inventoryInfo )
    {
        this.inventoryInfo = inventoryInfo;
    }
     */
}


ItemInfoBase.java
Code:
package my.package.model;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.Where;

/**
*
* @author por
*/
@Entity
@Table( name = "ITEMINFOTABLE" )
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@DiscriminatorColumn( name = "MODULETYPE",
                      discriminatorType = DiscriminatorType.INTEGER )
@Where( clause = "MYCONDITION" )
public abstract class ItemInfoBase implements Serializable
{
    private String                  itemId;
    private Float                   quantity;

    @Id
    @Type( type = "LegacyDBString" )
    @Column( name = "ITEMID" )
    public String getItemId()
    {
        return ( itemId );
    }

    public void setItemId( String itemId )
    {
        this.itemId = itemId;
    }

    @Column( name = "QUANTITY" )
    public Float getQuantity()
    {
        return quantity;
    }

    public void setQuantity( Float quantity )
    {
        this.quantity = quantity;
    }
}


ItemInfoPurchase.java
Code:

package my.package.model;

import java.io.Serializable;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import org.hibernate.annotations.Where;

/**
*
* @author por
*/
@Entity
@DiscriminatorValue( "0" )
@Where( clause = "MYCONDITION" )
public class ItemInfoPurchase extends ItemInfoBase implements Serializable
{
}


ItemInfoInventory.java
Code:

package my.package.model;

import java.io.Serializable;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import org.hibernate.annotations.Where;

/**
*
* @author por
*/
@Entity
@DiscriminatorValue( "1" )
@Where( clause = "MYCONDITION" )
public class ItemInfoInventory extends ItemInfoBase implements Serializable
{
}


ItemInfoSales.java
Code:
package my.package.model;

import java.io.Serializable;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import org.hibernate.annotations.Where;

/**
*
* @author por
*/
@Entity
@DiscriminatorValue( "2" )
@Where( clause = "MYCONDITION" )
public class ItemInfoSales extends ItemInfoBase implements Serializable
{
}


I'm attempting to fetch item's information from the database. Here's the code I'm using:
Snippet from ItemTestServlet.java
Code:
            Session session = HibernateContextListener.openSession();

            Item item = (Item) session.get( Item.class, itemId );
           
            if ( item != null ) {
                out.println( "Code: " + item.getId() );
                out.println( "Name: " + item.getName() );
                out.println( "Group: " + item.getGroupId() );

                ItemInfoSales sales = (ItemInfoSales) session.get( ItemInfoSales.class, item.getId() );
                out.println( "Sales info:" );
                out.println( "    Quantity: " + sales.getQuantity() );

                ItemInfoPurchase purch = (ItemInfoPurchase) session.get( ItemInfoPurchase.class, item.getId() );
                if ( purch != null ) {
                    out.println( "Purchase info:" );
                    out.println( "    Quantity: " + purch.getQuantity() );
                }

                ItemInfoInventory inv = (ItemInfoInventory) session.get( ItemInfoInventory.class, item.getId() );
                if ( inv != null ) {
                    out.println( "Inventory info:" );
                    out.println( "    Quantity: " + inv.getQuantity() );
                }
            }
            else {
                out.println( "Could not find item: " + itemId );
            }

            session.close();


In the previous tidbit, purch and inv are null! Here's the piece from the log:
Code:
FINE: loading entity: [my.package.model.ItemInfoSales#1120120]
FINE: about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
FINE: opening JDBC connection
FINE: returning the connector registry
FINE: poolmgr.no_resource_reference
FINE: returning the connector registry
FINE: In equals
FINE: Found/returing Connector descriptor in connector registry.
FINE: ConnectionMgr: poolName LegacyDBPool  txLevel : 1
FINE: Returning resourceManager
FINE: In getConnection
FINE: Returning resourceManager
FINE:
    select
        iteminfo0_.ITEMID as ITEMID4_0_,
        iteminfo0_.QUANTITY as QUANTITY4_0_
    from
        ITEMINFOTABLE iteminfo0_
    where
        iteminfo0_.ITEMID=?
        and iteminfo0_.MODULETYPE=2
INFO: Hibernate:
    select
        iteminfo0_.ITEMID as ITEMID4_0_,
        iteminfo0_.QUANTITY as QUANTITY4_0_
    from
        ITEMINFOTABLE iteminfo0_
    where
        iteminfo0_.ITEMID=?
        and iteminfo0_.MODULETYPE=2
FINEST: preparing statement
FINE: about to open ResultSet (open ResultSets: 0, globally: 0)
FINEST: processing result set
FINE: result set row: 0
FINE: result row: EntityKey[my.package.model.ItemInfoSales#1120120]
FINEST: Initializing object from ResultSet: [my.package.model.ItemInfoSales#1120120]
FINEST: Hydrating entity: [my.package.model.ItemInfoSales#1120120]
FINEST: returning '10.0' as column: QUANTITY4_0_
FINEST: done processing result set (1 rows)
FINE: about to close ResultSet (open ResultSets: 1, globally: 1)
FINE: about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
FINEST: closing statement
FINE: aggressively releasing JDBC connection
FINE: releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
FINE: Returning resourceManager
FINE: In cleanup
FINE: Pool: resourceClosed: 1
FINE:  Wait monitor is null
FINE: Pool: resourceFreed: 1
FINEST: total objects hydrated: 1
FINE: resolving associations for [my.package.model.ItemInfoSales#1120120]
FINE: done materializing entity [my.package.model.ItemInfoSales#1120120]
FINE: initializing non-lazy collections
FINE: done entity load
FINEST: after autocommit
FINE: aggressively releasing JDBC connection
FINEST: after transaction completion
FINEST: loading entity: [my.package.model.ItemInfoPurchase#1120120]
FINEST: attempting to resolve: [my.package.ItemInfoPurchase#1120120]
FINE: load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null
FINEST: after autocommit
FINE: aggressively releasing JDBC connection
FINEST: after transaction completion
FINEST: loading entity: [my.package.model.ItemInfoInventory#1120120]
FINEST: attempting to resolve: [my.package.model.ItemInfoInventory#1120120]
FINE: load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null
FINEST: after autocommit
FINE: aggressively releasing JDBC connection
FINEST: after transaction completion
FINEST: closing session


As you see, Hibernate does not even attempt to load the object from the database because of ItemInfoSales is in the cache, even though it's of different type than the object being loaded. If I use a different session for each ItemInfo type (i.e. close and reopen the Session between each session.get()), they get returned as expected.

What's wrong with my mapping of the classes that results in this inconsistency? I've spent two days trying to solve this, but I just can't get my head around it.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 17, 2009 9:05 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
Now the issue is that the id you have specified is not really unique.

As per the doc, if your discriminator column is also part of a composite identifier then set the discriminator to insert=false. Then map this column as part of the composite id.

See this:
http://www.hibernate.org/hib_docs/v3/re ... criminator

But i couldn't figure out how to achieve the same with annotations. But may be this will give you a pointer.

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 17, 2009 9:58 am 
Newbie

Joined: Tue Mar 17, 2009 3:27 am
Posts: 3
Thanks for the tip! I'm pretty confident that it pushed me in to the right direction, even though there's still some obstacles...

I changed the primary key of ItemInfoBase class to a composite key ItemInfoBasePrimaryKey which has both item id and module type. moduleType is annotated with @Column( name = "MODULETYPE", insertable = false, updatable = false ).

ItemInfoBasePrimaryKey.java:
Code:

package my.package.model;

import my.package.model.ItemInfoBase.ModuleType;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import org.hibernate.annotations.Type;

/**
*
* @author por
*/
@Embeddable
public class ItemInfoPrimaryKey implements Serializable
{
    private String      itemId;
    private ModuleType  moduleType;

    @Type( type = "LegacyDBString" )
    @Column( name = "ITEMID" )
    public String getItemId()
    {
        return itemId;
    }

    public void setItemId( String itemId )
    {
        this.itemId = itemId;
    }

    @Column( name = "MODULETYPE", insertable = false, updatable = false )
    public ModuleType getModuleType()
    {
        return moduleType;
    }

    public void setModuleType( ModuleType moduleType )
    {
        this.moduleType = moduleType;
    }

    @Override
    public boolean equals( Object obj )
    {
        if ( obj == null )
            return false;
        if ( getClass() != obj.getClass() )
            return false;
        final ItemInfoPrimaryKey other = (ItemInfoPrimaryKey) obj;
        if ( ( this.itemId == null ) ? ( other.itemId != null ) : !this.itemId.equals( other.itemId ) )
            return false;
        if ( this.moduleType != other.moduleType )
            return false;
        return true;
    }

    @Override
    public int hashCode()
    {
        int hash = 5;
        hash = 43 * hash + ( this.itemId != null ? this.itemId.hashCode() : 0 );
        hash = 43 * hash + ( this.moduleType != null ? this.moduleType.hashCode() : 0 );
        return hash;
    }
}



Now I'm getting this error in the log:

Code:
INFO: Hibernate Validator not found: ignoring
SEVERE: WebModule[/mypath]PWC1275: Exception sending context initialized event to listener instance of class my.package.HibernateContextListener
org.hibernate.MappingException: Repeated column in mapping for entity: my.package.model.ItemInfoSales column: MODULETYPE (should be mapped with insert="false" update="false")


There's something I must've missed... At least this gives me something to look information for.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 18, 2009 2:17 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
I think hibernate wont allow updatable insertable false for an ID field. You should be doing this on the discriminator field. But in annotation I could not find any way for this. If you get how to solve this, thn do let us know.

Also see this ticket http://opensource.atlassian.com/project ... se/HB-1149
This one has fixed the xml mapping for this. May be they forgot to fix the annotation counter part.

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 18, 2009 7:43 am 
Newbie

Joined: Tue Mar 17, 2009 3:27 am
Posts: 3
Thanks for the tip about the bug. It seems that it's as you guess - the annotation counterpart of discriminator is missing that same fix. I submitted a bug and we'll see how it goes.

I managed to get my test case working by annotating the base case with @MappedSuperclass, and repeating the @Table mapping in each subclass. This strategy prevents me from mapping the one-to-one relationship form e.g. Item->ItemInfoSales, but that's something I just have to live with.


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