I've flushed out a more detailed expression of the problem, so I figured, in the interests of clarity, I'd follow up my original post. The problem appears to come when trying to specify a <map />. I get a "Repeated column in mapping for collection" Exception, but I'm at a loss for how to write the mapping XML without having a repeated column.
Here's the table structure:
Code:
CREATE TABLE [dbo].[TestProduct] (
[ProductID] [int] IDENTITY (1, 1) NOT NULL ,
[Name] [nvarchar] (64) NOT NULL
)
CREATE TABLE [dbo].[TestStore] (
[StoreID] [int] IDENTITY (1, 1) NOT NULL ,
[Name] [nvarchar] (64) NOT NULL
)
CREATE TABLE [dbo].[TestPrice] (
[StoreID] [int] NOT NULL ,
[ProductID] [int] NOT NULL ,
[Price] [decimal](19, 4) NOT NULL ,
[NHVer] [int] NOT NULL
)
And here's the mapping so far, including the problem <map> tag:
Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="Com.Ipmcc.LineSvc.Test.CompKeyTest.Store, Com.Ipmcc.LineSvc.Test" table="TestStore">
<id name="StoreID" column="StoreID" type="Int32" unsaved-value="0">
<generator class="identity" />
</id>
<version name="NHVer" column="NHVer" unsaved-value="negative"/>
<property name="Name" column="Name" type="String" length="64"/>
<map name="ProductToPrice">
<key column="StoreID"/>
<index-many-to-many column="ProductID"
class="Com.Ipmcc.LineSvc.Test.CompKeyTest.Product, Com.Ipmcc.LineSvc.Test"/>
<many-to-many class="Com.Ipmcc.LineSvc.Test.CompKeyTest.Price, Com.Ipmcc.LineSvc.Test">
<column name="StoreID"/>
<column name="ProductID"/>
</many-to-many>
</map>
</class>
<class name="Com.Ipmcc.LineSvc.Test.CompKeyTest.Product, Com.Ipmcc.LineSvc.Test" table="TestProduct">
<id name="ProductID" column="ProductID" type="Int32" unsaved-value="0">
<generator class="identity" />
</id>
<property name="Name" column="Name" type="String" length="64"/>
<!-- inverse map mapping to go here when I figure it out. -->
</class>
<class name="Com.Ipmcc.LineSvc.Test.CompKeyTest.Price, Com.Ipmcc.LineSvc.Test" table="TestPrice">
<composite-id>
<key-many-to-one name="NHStore" column="StoreID"
class="Com.Ipmcc.LineSvc.Test.CompKeyTest.Store, Com.Ipmcc.LineSvc.Test" />
<key-many-to-one name="NHProduct" column="ProductID"
class="Com.Ipmcc.LineSvc.Test.CompKeyTest.Product, Com.Ipmcc.LineSvc.Test" />
</composite-id>
<version name="NHVer" column="NHVer" unsaved-value="negative"/>
<property name="PriceVal" column="Price" type="Decimal" />
</class>
</hibernate-mapping>
(C# entity object source at bottom)
Now, if I take the collection mapping out, everything works fine, so I'm comfortable that I've got the tricks of dealing with the composite key mapping. My unit tests are able to create and manipulate objects as expected, absent the collections. The goal here is to have an IDictionary on Store that takes a Product as an indexer and returns a Price object. The ultimate goal is to have a mirror image map on Product that takes a Store as an indexer and returns a price, but one step at a time.
The exception references StoreID as the 'repeated column' which I'm gathering stems from its use in bothe the <key/> element and the <many-to-many/> element. However, for how I read the documentation, this is all as it should be. The column referenced in <key/> is the FK (part of composite key) to the owning (collection-having) class, in this case StoreID. The <index-many-to-many/> element is the foreign key column for the collection index values, in this case ProductID. The <many-to-many/> element contains column elements for each column making up the composite primary key, which seemed in line with the example I found in Hibernate in Action.
So that's where I'm at. Is this a situation that's just not supported? If its just a semantic limitation, I was thinking perhaps I could map Price to a SQL view that had duplicates (with different names) of the StoreID and ProductID columns, so I could write the mapping without duplicating columns, but I figured I'd ask if there isn't some greater problem/challenge here that goes deeper.
Thanks for any input.
Ian
C# Entity object source:
Code:
public class Store
{
private int m_StoreID;
private string m_Name;
private Int32 m_NHVer;
private System.Collections.IDictionary m_ProductToPrice;
public Store()
{
m_StoreID = 0;
m_Name = String.Empty;
m_NHVer = -1;
}
public string Name
{
get { return m_Name; }
set
{
if (value != null && value.Length > 32)
throw new ArgumentOutOfRangeException("Invalid value for Name", value, value.ToString());
m_Name = value;
}
}
public int StoreID
{
get { return m_StoreID; }
set { m_StoreID = value; }
}
private Int32 NHVer
{
get { return m_NHVer; }
set { m_NHVer = value; }
}
public System.Collections.IDictionary ProductToPrice
{
get
{
return this.m_ProductToPrice;
}
set
{
this.m_ProductToPrice = value;
}
}
}
public class Product
{
private int m_ProductID;
private string m_Name;
public string Name
{
get { return m_Name; }
set
{
if (value != null && value.Length > 32)
throw new ArgumentOutOfRangeException("Invalid value for Name", value, value.ToString());
m_Name = value;
}
}
public int ProductID
{
get { return m_ProductID; }
set { m_ProductID = value; }
}
public Product()
{
m_ProductID = 0;
m_Name = String.Empty;
}
}
public class Price
{
private NHibernate.Generics.EntityRef<Product> m_Product;
private NHibernate.Generics.EntityRef<Store> m_Store;
private decimal m_Price;
private Int32 m_NHVer;
protected Price()
{
m_Product = new NHibernate.Generics.EntityRef<Product>();
m_Store = new NHibernate.Generics.EntityRef<Store>();
m_Price = 0.0M;
m_NHVer = -1;
}
public Price(Store store, Product product) : this()
{
if (store == null) throw new ArgumentNullException("store");
if (product == null) throw new ArgumentNullException("product");
this.NHStore = store;
this.NHProduct = product;
}
public Product Product
{
get { return NHProduct; }
}
private Product NHProduct
{
get { return m_Product.Value; }
set { m_Product.Value = value; }
}
public Store Store
{
get { return NHStore; }
}
private Store NHStore
{
get { return m_Store.Value; }
set { m_Store.Value = value; }
}
public decimal PriceVal
{
get { return m_Price; }
set { m_Price = value; }
}
private Int32 NHVer
{
get { return m_NHVer; }
set { m_NHVer = value; }
}
public override bool Equals(object obj)
{
if (this == obj)
return true;
Price theObj = obj as Price;
if (theObj == null)
return false;
return (theObj.Store == this.Store && theObj.Product == this.Product && theObj.PriceVal == this.PriceVal && theObj.NHVer == this.NHVer);
}
public override int GetHashCode()
{
if (NHVer == -1)
return base.GetHashCode();
else
return Store.GetHashCode() ^ Product.GetHashCode();
}
}