如何配置 nHibernate 进行多对多映射?
使用 nHibernate 3.2、C# 4.0、SQL Server 2008 R2 Express
我有两个业务实体 - Broker
和 Market
。它们分别存储在brokers
和markets
表中。我还有 brokerMarkets
表,其中有一个名为 MinIncrement
的额外列。 Broker 和 Market 之间存在多对多关系,但仅当我想要存储 MinIncrement 值时(即它是可选的)。我的类如下所示:
public class Market : BusinessBase
{
public Market() {}
public virtual int Id { get; set; }
public virtual string Symbol { get; set; }
public virtual string Description { get; set; }
}
public class Broker : BusinessBase
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual bool IsDefault { get; set; }
public virtual bool IsActive { get; set; }
public virtual ISet<Account> Accounts { get; set; }
}
public class BrokerMarket : Market
{
public BrokerMarket() { }
public virtual Broker Broker {get; set;}
public virtual decimal MinIncrement { get; set; }
}
我的映射文件如下所示: Market.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Market,MooDB" table="markets">
<id name="Id" column="marketId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Symbol" column="symbol" type="String" length="10" not-null="true" />
<property name="Description" column="description" type="String" length="30" not-null="true" />
<set name="Brokers" generic="true" table="brokerMarkets">
<key column="marketId" />
<many-to-many column="brokerId" class="MooDB.BusinessLayer.Broker,MooDB" />
</set>
</class>
</hibernate-mapping>
Broker.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Broker,MooDB" table="brokers">
<id name="Id" column="brokerId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Name" column="broker" type="String" length="50" not-null="true" />
<property name="IsActive" column="isActive" type="bool" not-null="true" />
<property name="IsDefault" column="isDefault" type="bool" not-null="true" />
<set name="Markets" generic="true" table="brokerMarkets">
<key column="brokerId" />
<many-to-many column="marketId" class="MooDB.BusinessLayer.Market,MooDB" />
</set>
<set name="Accounts" table="accounts" generic="true" inverse="true">
<key column="brokerId" />
<one-to-many class="MooDB.BusinessLayer.Account,MooDB" />
</set>
</class>
</hibernate-mapping>
在我的数据访问层中,我有一个添加 BrokerMarket 的方法:
public void AddBrokerMarket(BrokerMarket bm)
{
using (ITransaction tx = _session.BeginTransaction())
{
try
{
_session.Save(bm);
_session.Flush();
tx.Commit();
}
catch (HibernateException)
{
tx.Rollback();
throw;
}
}
}
在我的单元测试中,我尝试添加一个 BrokerMarket,如下所示:
[Test]
public void CanAddBrokerMarket()
{
Broker broker = _provider.GetBrokerById(1);
Market market = _provider.GetMarketById(2);
var brokerMarket = new BrokerMarket { Broker = broker, Description = market.Description, Symbol = market.Symbol, MinIncrement = 0.01M };
_provider.AddBrokerMarket(brokerMarket);
}
当我运行测试时,我收到此错误:
运行测试。测试 “MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket”失败: 消息 NHibernate.MappingException:没有持久化: MooDB.BusinessLayer.BrokerMarket 堆栈跟踪位于 NHibernate.Impl.SessionFactoryImpl.GetEntityPersister(字符串 实体名称)在NHibernate.Impl.SessionImpl.GetEntityPersister(字符串 实体名称,对象 obj) at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGenerateId(对象 实体、字符串实体名称、对象任何内容、IEventSource 源、 布尔值 requireImmediateIdAccess) at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGenerateOrRequestedId(SaveOrUpdateEvent 事件)在 NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneeratedOrRequestedId(SaveOrUpdateEvent 事件)在 NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent 事件)在 NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent 事件)在 NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent 事件)在NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent 事件)在 NHibernate.Impl.SessionImpl.Save(Object obj) NHibernateDataProvider.cs(91,0):位于 MooDB.DataLayer.NHibernateDataProvider.AddBrokerMarket(BrokerMarket bm) DataLayerTests.cs(81,0): 在 MooDB.Tests.DataLayerTests.CanAddBrokerMarket()
我不确定我做错了什么。我是否需要在 Market
类中添加 Broker
集合,并在 Broker
类中添加 Market
集合?我确实尝试过,但遇到了同样的错误,而且我认为我在概念上遗漏了一些东西。
更新
我已实施以下更改: Market.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Market,MooDB" table="markets">
<id name="Id" column="marketId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Symbol" column="symbol" type="String" length="10" not-null="true" />
<property name="Description" column="description" type="String" length="30" not-null="true" />
<map name="BrokerMarkets" table="brokerMarkets" lazy="true" cascade="none">
<cache usage="read-write"/>
<key column="marketId" />
<index column="brokerId" type="Int32" />
<composite-element class="MooDB.BusinessLayer.BrokerMarket,MooDB">
<parent name="Market"/>
<property name="MinIncrement" column="minIncrement" type="decimal" />
<many-to-one name="Broker" class="MooDB.BusinessLayer.Broker,MooDB" column="brokerId" />
</composite-element>
</map>
</class>
</hibernate-mapping>
Broker.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Broker,MooDB" table="brokers">
<id name="Id" column="brokerId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Name" column="broker" type="String" length="50" not-null="true" />
<property name="IsActive" column="isActive" type="bool" not-null="true" />
<property name="IsDefault" column="isDefault" type="bool" not-null="true" />
<set name="Accounts" table="accounts" generic="true" inverse="true">
<key column="brokerId" />
<one-to-many class="MooDB.BusinessLayer.Account,MooDB" />
</set>
</class>
</hibernate-mapping>
Market
类:
public class Market : BusinessBase
{
public Market() {}
public virtual int Id { get; set; }
public virtual string Symbol { get; set; }
public virtual string Description { get; set; }
public virtual ISet<BrokerMarket> BrokerMarkets { get; set; } //added this line
}
BrokerMarket
类
public class BrokerMarket : Market
{
public BrokerMarket() { }
public virtual Broker Broker {get; set;}
public virtual Market Market { get; set; } // added this line
public virtual decimal MinIncrement { get; set; }
}
其他一切均未更改。我再次运行单元测试,得到:
运行测试。测试 “MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket”失败: 消息 TestFixtureSetUp 在 DataLayerTests 中失败
测试“MooDBTests/MooDB/Tests/DataLayerTests”失败:消息设置: NHibernate.MappingException:映射中的重复列 集合:MooDB.BusinessLayer.Market.BrokerMarkets 列:brokerId
UPDATE 2
如果我从市场中删除
。 hbm.xml 运行单元测试时出现此错误:
运行测试。测试 “MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket”失败: 消息 TestFixtureSetUp 在 DataLayerTests 中失败
测试“MooDBTests/MooDB/Tests/DataLayerTests”失败:消息设置: NHibernate.MappingException:MooDB.Mappings.Market.hbm.xml(17,10): XML 验证错误:命名空间中的元素“map” 'urn:nhibernate-mapping-2.2' 具有无效的子元素 命名空间“urn:nhibernate-mapping-2.2”中的“复合元素”。名单 预期可能的元素:'map-key,composite-map-key, 映射键多对多、索引、复合索引、索引多对多、 命名空间“urn:nhibernate-mapping-2.2”中的索引多对任意。 ----> System.Xml.Schema.XmlSchemaValidationException:元素“map” 命名空间“urn:nhibernate-mapping-2.2”具有无效的子元素 命名空间“urn:nhibernate-mapping-2.2”中的“复合元素”。名单 预期可能的元素:'map-key,composite-map-key, 映射键多对多、索引、复合索引、索引多对多、 命名空间“urn:nhibernate-mapping-2.2”中的“索引多对任意”。
Using nHibernate 3.2, C# 4.0, SQL Server 2008 R2 Express
I have two business entities - Broker
and Market
. They are stored in the brokers
and markets
tables respectively. I also have brokerMarkets
table with one extra column called MinIncrement
. There's a many-many relationship between Broker and Market, but only when I want to store a MinIncrement value (i.e. it's optional). My classes look like this:
public class Market : BusinessBase
{
public Market() {}
public virtual int Id { get; set; }
public virtual string Symbol { get; set; }
public virtual string Description { get; set; }
}
public class Broker : BusinessBase
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual bool IsDefault { get; set; }
public virtual bool IsActive { get; set; }
public virtual ISet<Account> Accounts { get; set; }
}
public class BrokerMarket : Market
{
public BrokerMarket() { }
public virtual Broker Broker {get; set;}
public virtual decimal MinIncrement { get; set; }
}
My mapping files look like this:
Market.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Market,MooDB" table="markets">
<id name="Id" column="marketId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Symbol" column="symbol" type="String" length="10" not-null="true" />
<property name="Description" column="description" type="String" length="30" not-null="true" />
<set name="Brokers" generic="true" table="brokerMarkets">
<key column="marketId" />
<many-to-many column="brokerId" class="MooDB.BusinessLayer.Broker,MooDB" />
</set>
</class>
</hibernate-mapping>
Broker.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Broker,MooDB" table="brokers">
<id name="Id" column="brokerId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Name" column="broker" type="String" length="50" not-null="true" />
<property name="IsActive" column="isActive" type="bool" not-null="true" />
<property name="IsDefault" column="isDefault" type="bool" not-null="true" />
<set name="Markets" generic="true" table="brokerMarkets">
<key column="brokerId" />
<many-to-many column="marketId" class="MooDB.BusinessLayer.Market,MooDB" />
</set>
<set name="Accounts" table="accounts" generic="true" inverse="true">
<key column="brokerId" />
<one-to-many class="MooDB.BusinessLayer.Account,MooDB" />
</set>
</class>
</hibernate-mapping>
In my data access layer I have a method to add a BrokerMarket:
public void AddBrokerMarket(BrokerMarket bm)
{
using (ITransaction tx = _session.BeginTransaction())
{
try
{
_session.Save(bm);
_session.Flush();
tx.Commit();
}
catch (HibernateException)
{
tx.Rollback();
throw;
}
}
}
In my unit test I try and add a BrokerMarket like this:
[Test]
public void CanAddBrokerMarket()
{
Broker broker = _provider.GetBrokerById(1);
Market market = _provider.GetMarketById(2);
var brokerMarket = new BrokerMarket { Broker = broker, Description = market.Description, Symbol = market.Symbol, MinIncrement = 0.01M };
_provider.AddBrokerMarket(brokerMarket);
}
When I run the test I get this error:
Running the tests. Test
'MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket' failed:
Message NHibernate.MappingException : No persister for:
MooDB.BusinessLayer.BrokerMarket Stack Trace at
NHibernate.Impl.SessionFactoryImpl.GetEntityPersister(String
entityName) at NHibernate.Impl.SessionImpl.GetEntityPersister(String
entityName, Object obj) at
NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object
entity, String entityName, Object anything, IEventSource source,
Boolean requiresImmediateIdAccess) at
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent
event) at
NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent
event) at
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent
event) at
NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent
event) at
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent
event) at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent
event) at NHibernate.Impl.SessionImpl.Save(Object obj)
NHibernateDataProvider.cs(91,0): at
MooDB.DataLayer.NHibernateDataProvider.AddBrokerMarket(BrokerMarket
bm) DataLayerTests.cs(81,0): at
MooDB.Tests.DataLayerTests.CanAddBrokerMarket()
I'm not sure what I've done wrong. Do I need to add a Broker
collection in the Market
class and a Market
collection in the Broker
class? I did try that but I get the same error, and I think I'm missing something conceptually somewhere.
UPDATE
I have implemented the following changes:
Market.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Market,MooDB" table="markets">
<id name="Id" column="marketId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Symbol" column="symbol" type="String" length="10" not-null="true" />
<property name="Description" column="description" type="String" length="30" not-null="true" />
<map name="BrokerMarkets" table="brokerMarkets" lazy="true" cascade="none">
<cache usage="read-write"/>
<key column="marketId" />
<index column="brokerId" type="Int32" />
<composite-element class="MooDB.BusinessLayer.BrokerMarket,MooDB">
<parent name="Market"/>
<property name="MinIncrement" column="minIncrement" type="decimal" />
<many-to-one name="Broker" class="MooDB.BusinessLayer.Broker,MooDB" column="brokerId" />
</composite-element>
</map>
</class>
</hibernate-mapping>
Broker.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MooDB"
namespace="MooDB.BusinessLayer">
<class name="MooDB.BusinessLayer.Broker,MooDB" table="brokers">
<id name="Id" column="brokerId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" column="version" type="integer" unsaved-value="0" />
<property name="Name" column="broker" type="String" length="50" not-null="true" />
<property name="IsActive" column="isActive" type="bool" not-null="true" />
<property name="IsDefault" column="isDefault" type="bool" not-null="true" />
<set name="Accounts" table="accounts" generic="true" inverse="true">
<key column="brokerId" />
<one-to-many class="MooDB.BusinessLayer.Account,MooDB" />
</set>
</class>
</hibernate-mapping>
Market
class:
public class Market : BusinessBase
{
public Market() {}
public virtual int Id { get; set; }
public virtual string Symbol { get; set; }
public virtual string Description { get; set; }
public virtual ISet<BrokerMarket> BrokerMarkets { get; set; } //added this line
}
BrokerMarket
class
public class BrokerMarket : Market
{
public BrokerMarket() { }
public virtual Broker Broker {get; set;}
public virtual Market Market { get; set; } // added this line
public virtual decimal MinIncrement { get; set; }
}
Everything else is unchanged. I run the unit test again and I get:
Running the tests. Test
'MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket' failed:
Message TestFixtureSetUp failed in DataLayerTestsTest 'MooDBTests/MooDB/Tests/DataLayerTests' failed: Message SetUp :
NHibernate.MappingException : Repeated column in mapping for
collection: MooDB.BusinessLayer.Market.BrokerMarkets column: brokerId
UPDATE 2
If I remove
<index column="brokerId" type="Int32" />
from Market.hbm.xml I get this error when running the unit test:
Running the tests. Test
'MooDBTests/MooDB/Tests/DataLayerTests/CanAddBrokerMarket' failed:
Message TestFixtureSetUp failed in DataLayerTestsTest 'MooDBTests/MooDB/Tests/DataLayerTests' failed: Message SetUp :
NHibernate.MappingException : MooDB.Mappings.Market.hbm.xml(17,10):
XML validation error: The element 'map' in namespace
'urn:nhibernate-mapping-2.2' has invalid child element
'composite-element' in namespace 'urn:nhibernate-mapping-2.2'. List of
possible elements expected: 'map-key, composite-map-key,
map-key-many-to-many, index, composite-index, index-many-to-many,
index-many-to-any' in namespace 'urn:nhibernate-mapping-2.2'. ---->
System.Xml.Schema.XmlSchemaValidationException : The element 'map' in
namespace 'urn:nhibernate-mapping-2.2' has invalid child element
'composite-element' in namespace 'urn:nhibernate-mapping-2.2'. List of
possible elements expected: 'map-key, composite-map-key,
map-key-many-to-many, index, composite-index, index-many-to-many,
index-many-to-any' in namespace 'urn:nhibernate-mapping-2.2'.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
该错误可能是因为您尚未定义 BrokerMarket 类的映射,如果您想像您所做的那样保存经纪商市场,则必须创建 BrokerMarket.hbm.xml。您还可以看看在这里使用复合元素,例如 http://thesoftwaresimpleton.blogspot.com/2010/03/nhibernate-many-to-many-with-extra.html
the error could be because you have not defined mapping for BrokerMarket Class, If you want to save broker market like the way you are doin will have to create BrokerMarket.hbm.xml. you may also have a look at using composite element here something like http://thesoftwaresimpleton.blogspot.com/2010/03/nhibernate-many-to-many-with-extra.html