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:
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.javaCode:
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.javaCode:
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.javaCode:
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.javaCode:
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.javaCode:
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.