使用 NHibernate 映射复合键的问题(已添加列)
Problem mapping composite key with NHibernate (column already been added)
NHibernate(和 Hibernate 就此而言)的新手,我正在努力解决复合键问题。这是部分数据库设计的简化版本。
table_a
+---------------------+
| * a_id varcha (10) | table_z
| label varchar(50) | +----------------------+
| +<----------------+ * a_id varchar(10) |
+---------------------+ +----------| * b_id varchar(10) |
| +------+ * c_id varchar(10) |
table_b | | | name varchar(100) |
+---------------------+ | | | |
| * b_id varcha (10) | | | +----------------------+
| label varchar(50) <------+ |
| | |
+---------------------+ |
|
table_c |
+---------------------+ |
| * c_id varcha (10) <----------+
| label varchar(50) |
| |
+---------------------+
这里的关键元素是 table_z 主键是 table a,b,c 的 3 个主键的组合(因此,它控制 a,b 和 c 的唯一组合) .他们也分别 FK 到 table_a、table_b 和 table_c。
现在,除了数据库设计考虑之外,有没有办法将其映射到 NHibernate。我的尝试导致堆栈跟踪抱怨 "ArgumentException: The column 'a_id' has already been added in this SQL builder"。谷歌搜索告诉我问题是我在连接的两端使用了相同的字段名称。我很惊讶这甚至是一个问题 - 或者我完全误解了这个问题..
这是 DDL (Postgresql)
CREATE TABLE test.table_a(
a_id varchar(10) primary key,
label varchar(50)
);
CREATE TABLE test.table_b(
b_id varchar(10) primary key,
label varchar(50)
);
CREATE TABLE test.table_c(
c_id varchar(10) primary key,
label varchar(50)
);
CREATE TABLE test.table_z(
a_id varchar(10),
b_id varchar(10),
c_id varchar(10),
name varchar(100)
);
-- add combined primary key on table_z
ALTER TABLE test.table_z ADD CONSTRAINT pk_z_combined
PRIMARY KEY (a_id,b_id,c_id)
;
-- FK
ALTER TABLE test.table_z ADD CONSTRAINT FK_to_a
FOREIGN KEY (a_id) REFERENCES test.table_a (a_id) ON DELETE No Action ON UPDATE No Action;
ALTER TABLE test.table_z ADD CONSTRAINT FK_to_b
FOREIGN KEY (b_id) REFERENCES test.table_b (b_id) ON DELETE No Action ON UPDATE No Action;
ALTER TABLE test.table_z ADD CONSTRAINT FK_to_c
FOREIGN KEY (c_id) REFERENCES test.table_c (c_id) ON DELETE No Action ON UPDATE No Action;
这是 Fluent Hibernate C# 代码
using FluentNHibernate.Mapping;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace pm
{
class TestAMapping : ClassMap<TestA>
{
public TestAMapping()
{
Table("test.table_a");
Id(x => x.Id, "a_id");
Map(x => x.Label, "label");
}
}
class TestBMapping : ClassMap<TestB>
{
public TestBMapping()
{
Table("test.table_b");
Id(x => x.Id, "b_id");
Map(x => x.Label, "label");
}
}
class TestCMapping : ClassMap<TestC>
{
public TestCMapping()
{
Table("test.table_c");
Id(x => x.Id, "c_id");
Map(x => x.Label, "label");
}
}
class TestZMapping : ClassMap<TestZ>
{
public TestZMapping()
{
Table("test.table_z");
CompositeId()
.KeyProperty(x => x.Aid, "a_id")
.KeyProperty(x => x.Bid, "b_id")
.KeyProperty(x => x.Cid, "c_id");
Map(x => x.Name, "name");
References(x => x.TestAObj).Column("a_id");
References(x => x.TestBObj).Column("b_id");
References(x => x.TestCObj).Column("c_id");
}
}
class TestA
{
public virtual string Id { get; set; }
public virtual string Label { get; set; }
}
class TestB
{
public virtual string Id { get; set; }
public virtual string Label { get; set; }
}
class TestC
{
public virtual string Id { get; set; }
public virtual string Label { get; set; }
}
class TestZ
{
public virtual string Aid { get; set; }
public virtual string Bid { get; set; }
public virtual string Cid { get; set; }
public virtual string Name { get; set; }
public virtual TestA TestAObj { get; set; }
public virtual TestB TestBObj { get; set; }
public virtual TestC TestCObj { get; set; }
//
public override bool Equals(object obj)
{
var other = obj as TestZ;
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.Aid == other.Aid &&
this.Bid == other.Bid && this.Cid == other.Cid;
}
public override int GetHashCode()
{
unchecked
{
int hash = GetType().GetHashCode();
hash = (hash * 31) ^ Aid.GetHashCode();
hash = (hash * 31) ^ Bid.GetHashCode();
hash = (hash * 31) ^ Cid.GetHashCode();
return hash;
}
}
}
}
相关堆栈跟踪
FluentNHibernate.Cfg.FluentConfigurationException
HResult=0x80131500
Message=An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
Source=FluentNHibernate
StackTrace:
at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory()
at pm.dal.DAL.CreateSessionFactory(String connectionString) in C:\Users\Laptop\source\repos\pm\dal\DAL.cs:line 49
at pm.dal.DAL..ctor(String connectionString) in C:\Users\Laptop\source\repos\pm\dal\DAL.cs:line 41
at pm.Manager.Manager.Connect(String connectionString) in C:\Users\Laptop\source\repos\pm\Manager\Manager.cs:line 102
(blah...)
Inner Exception 1:
MappingException: Unable to build the insert statement for class pm.TestZ: a failure occured when adding the Id of the class
Inner Exception 2:
ArgumentException: The column 'a_id' has already been added in this SQL builder
Parameter name: columnName
谁能告诉我我错在哪里..
感谢堆
我只是看看我在我的项目中是如何做到的。不幸的是,我无法描述为什么要这样做 ;-)
public TestZMapping()
{
Table("test.table_z");
CompositeId()
.KeyProperty(x => x.Aid, "a_id")
.KeyProperty(x => x.Bid, "b_id")
.KeyProperty(x => x.Cid, "c_id");
Map(x => x.Name, "name");
References(x => x.TestAObj).Column("a_id").Not.Insert().Not.Update();
References(x => x.TestBObj).Column("b_id").Not.Insert().Not.Update();
References(x => x.TestCObj).Column("c_id").Not.Insert().Not.Update();
}
NHibernate 5 强制您为更新数据库字段定义明确的责任 - 特别是当您有多个属性访问同一字段时。只允许一个 属性 映射是可写的 - 作为 "new value".
的所有者
复合键并没有真正定义 class 的属性——它只是定义了一个复合键。定义一个 key-属性 不允许你,例如在查询中对 属性 进行排序。因此,您必须在 属性 列表中重复此 属性。但是这种重复必须是只读的,以赋予 key-属性 更新值的责任。
具有重复属性的 NHibernate 4 => 允许,但可能会导致意外行为
<composite-id class="MyClass, MyDll" >
<key-property name="Key1" />
<key-property name="Key2"/>
</composite-id>
<property name="Key1" />
<property name="Key2" />
<property name="SomeProperty" />
<property name="SomeMoreKey1" column="Key1" />
N 休眠 5
<!-- not sortable by Key1 or Key2, because property is not known -->
<composite-id class="MyClass, MyDll" >
<key-property name="Key1" />
<key-property name="Key2"/>
</composite-id>
<!-- no allowed to repeat the keys as property => "has already been defined"
<property name="SomeProperty" />
NHibernate 5 具有属性
<!-- readonly "repeated" properties -->
<composite-id class="MyClass, MyDll" >
<key-property name="Key1" />
<key-property name="Key2"/>
</composite-id>
<property name="Key1" insert="false" update="false"/>
<property name="Key2" insert="false" update="false"/>
<property name="SomeProperty" />
<property name="SomeMoreKey1" column="Key1" insert="false" update="false"/>
如果您需要对同一列同时使用 ComposedId 和 ManyToOne,并且出现错误:
Unknown column 'SomeId' in 'field list'
或
The column 'SomeId' has already been added in this SQL builder
Parameter name: columnName
然后将 ManyToOne 放入 ComposedId 映射器中。像这样:
internal class PricesMapping : ClassMapping<PriceEntity>
{
public PricesMapping()
{
Table("Prices");
ComposedId(m =>
{
m.ManyToOne(x => x.PriceBlock, map =>
{
map.Column("PriceBlockId");
});
m.Property(x => x.CurrencyCode);
});
Property(x => x.Value, map => map.Column("Price"));
}
}
NHibernate(和 Hibernate 就此而言)的新手,我正在努力解决复合键问题。这是部分数据库设计的简化版本。
table_a
+---------------------+
| * a_id varcha (10) | table_z
| label varchar(50) | +----------------------+
| +<----------------+ * a_id varchar(10) |
+---------------------+ +----------| * b_id varchar(10) |
| +------+ * c_id varchar(10) |
table_b | | | name varchar(100) |
+---------------------+ | | | |
| * b_id varcha (10) | | | +----------------------+
| label varchar(50) <------+ |
| | |
+---------------------+ |
|
table_c |
+---------------------+ |
| * c_id varcha (10) <----------+
| label varchar(50) |
| |
+---------------------+
这里的关键元素是 table_z 主键是 table a,b,c 的 3 个主键的组合(因此,它控制 a,b 和 c 的唯一组合) .他们也分别 FK 到 table_a、table_b 和 table_c。
现在,除了数据库设计考虑之外,有没有办法将其映射到 NHibernate。我的尝试导致堆栈跟踪抱怨 "ArgumentException: The column 'a_id' has already been added in this SQL builder"。谷歌搜索告诉我问题是我在连接的两端使用了相同的字段名称。我很惊讶这甚至是一个问题 - 或者我完全误解了这个问题..
这是 DDL (Postgresql)
CREATE TABLE test.table_a(
a_id varchar(10) primary key,
label varchar(50)
);
CREATE TABLE test.table_b(
b_id varchar(10) primary key,
label varchar(50)
);
CREATE TABLE test.table_c(
c_id varchar(10) primary key,
label varchar(50)
);
CREATE TABLE test.table_z(
a_id varchar(10),
b_id varchar(10),
c_id varchar(10),
name varchar(100)
);
-- add combined primary key on table_z
ALTER TABLE test.table_z ADD CONSTRAINT pk_z_combined
PRIMARY KEY (a_id,b_id,c_id)
;
-- FK
ALTER TABLE test.table_z ADD CONSTRAINT FK_to_a
FOREIGN KEY (a_id) REFERENCES test.table_a (a_id) ON DELETE No Action ON UPDATE No Action;
ALTER TABLE test.table_z ADD CONSTRAINT FK_to_b
FOREIGN KEY (b_id) REFERENCES test.table_b (b_id) ON DELETE No Action ON UPDATE No Action;
ALTER TABLE test.table_z ADD CONSTRAINT FK_to_c
FOREIGN KEY (c_id) REFERENCES test.table_c (c_id) ON DELETE No Action ON UPDATE No Action;
这是 Fluent Hibernate C# 代码
using FluentNHibernate.Mapping;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace pm
{
class TestAMapping : ClassMap<TestA>
{
public TestAMapping()
{
Table("test.table_a");
Id(x => x.Id, "a_id");
Map(x => x.Label, "label");
}
}
class TestBMapping : ClassMap<TestB>
{
public TestBMapping()
{
Table("test.table_b");
Id(x => x.Id, "b_id");
Map(x => x.Label, "label");
}
}
class TestCMapping : ClassMap<TestC>
{
public TestCMapping()
{
Table("test.table_c");
Id(x => x.Id, "c_id");
Map(x => x.Label, "label");
}
}
class TestZMapping : ClassMap<TestZ>
{
public TestZMapping()
{
Table("test.table_z");
CompositeId()
.KeyProperty(x => x.Aid, "a_id")
.KeyProperty(x => x.Bid, "b_id")
.KeyProperty(x => x.Cid, "c_id");
Map(x => x.Name, "name");
References(x => x.TestAObj).Column("a_id");
References(x => x.TestBObj).Column("b_id");
References(x => x.TestCObj).Column("c_id");
}
}
class TestA
{
public virtual string Id { get; set; }
public virtual string Label { get; set; }
}
class TestB
{
public virtual string Id { get; set; }
public virtual string Label { get; set; }
}
class TestC
{
public virtual string Id { get; set; }
public virtual string Label { get; set; }
}
class TestZ
{
public virtual string Aid { get; set; }
public virtual string Bid { get; set; }
public virtual string Cid { get; set; }
public virtual string Name { get; set; }
public virtual TestA TestAObj { get; set; }
public virtual TestB TestBObj { get; set; }
public virtual TestC TestCObj { get; set; }
//
public override bool Equals(object obj)
{
var other = obj as TestZ;
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.Aid == other.Aid &&
this.Bid == other.Bid && this.Cid == other.Cid;
}
public override int GetHashCode()
{
unchecked
{
int hash = GetType().GetHashCode();
hash = (hash * 31) ^ Aid.GetHashCode();
hash = (hash * 31) ^ Bid.GetHashCode();
hash = (hash * 31) ^ Cid.GetHashCode();
return hash;
}
}
}
}
相关堆栈跟踪
FluentNHibernate.Cfg.FluentConfigurationException
HResult=0x80131500
Message=An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
Source=FluentNHibernate
StackTrace:
at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory()
at pm.dal.DAL.CreateSessionFactory(String connectionString) in C:\Users\Laptop\source\repos\pm\dal\DAL.cs:line 49
at pm.dal.DAL..ctor(String connectionString) in C:\Users\Laptop\source\repos\pm\dal\DAL.cs:line 41
at pm.Manager.Manager.Connect(String connectionString) in C:\Users\Laptop\source\repos\pm\Manager\Manager.cs:line 102
(blah...)
Inner Exception 1:
MappingException: Unable to build the insert statement for class pm.TestZ: a failure occured when adding the Id of the class
Inner Exception 2:
ArgumentException: The column 'a_id' has already been added in this SQL builder
Parameter name: columnName
谁能告诉我我错在哪里..
感谢堆
我只是看看我在我的项目中是如何做到的。不幸的是,我无法描述为什么要这样做 ;-)
public TestZMapping()
{
Table("test.table_z");
CompositeId()
.KeyProperty(x => x.Aid, "a_id")
.KeyProperty(x => x.Bid, "b_id")
.KeyProperty(x => x.Cid, "c_id");
Map(x => x.Name, "name");
References(x => x.TestAObj).Column("a_id").Not.Insert().Not.Update();
References(x => x.TestBObj).Column("b_id").Not.Insert().Not.Update();
References(x => x.TestCObj).Column("c_id").Not.Insert().Not.Update();
}
NHibernate 5 强制您为更新数据库字段定义明确的责任 - 特别是当您有多个属性访问同一字段时。只允许一个 属性 映射是可写的 - 作为 "new value".
的所有者复合键并没有真正定义 class 的属性——它只是定义了一个复合键。定义一个 key-属性 不允许你,例如在查询中对 属性 进行排序。因此,您必须在 属性 列表中重复此 属性。但是这种重复必须是只读的,以赋予 key-属性 更新值的责任。
具有重复属性的 NHibernate 4 => 允许,但可能会导致意外行为
<composite-id class="MyClass, MyDll" >
<key-property name="Key1" />
<key-property name="Key2"/>
</composite-id>
<property name="Key1" />
<property name="Key2" />
<property name="SomeProperty" />
<property name="SomeMoreKey1" column="Key1" />
N 休眠 5
<!-- not sortable by Key1 or Key2, because property is not known -->
<composite-id class="MyClass, MyDll" >
<key-property name="Key1" />
<key-property name="Key2"/>
</composite-id>
<!-- no allowed to repeat the keys as property => "has already been defined"
<property name="SomeProperty" />
NHibernate 5 具有属性
<!-- readonly "repeated" properties -->
<composite-id class="MyClass, MyDll" >
<key-property name="Key1" />
<key-property name="Key2"/>
</composite-id>
<property name="Key1" insert="false" update="false"/>
<property name="Key2" insert="false" update="false"/>
<property name="SomeProperty" />
<property name="SomeMoreKey1" column="Key1" insert="false" update="false"/>
如果您需要对同一列同时使用 ComposedId 和 ManyToOne,并且出现错误:
Unknown column 'SomeId' in 'field list'
或
The column 'SomeId' has already been added in this SQL builder Parameter name: columnName
然后将 ManyToOne 放入 ComposedId 映射器中。像这样:
internal class PricesMapping : ClassMapping<PriceEntity>
{
public PricesMapping()
{
Table("Prices");
ComposedId(m =>
{
m.ManyToOne(x => x.PriceBlock, map =>
{
map.Column("PriceBlockId");
});
m.Property(x => x.CurrencyCode);
});
Property(x => x.Value, map => map.Column("Price"));
}
}