postgresql 使用NHibernateMap复合键时出现问题(已添加列)

af7jpaap  于 2023-06-29  发布在  PostgreSQL
关注(0)|答案(4)|浏览(112)

我是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) |
 |                     |
 +---------------------+

这里的key元素是table_z primary key是表a,b,c的3个主键的组合(因此,它控制a,b和c的唯一组合)。它们也分别FK到table_a、table_b和table_c。
现在,除了数据库设计考虑之外,是否有一种方法可以将其Map到NHibernate中。我的尝试导致堆栈跟踪报告“ArgumentException:列'a_id'已添加到此SQL生成器”中。在谷歌上搜索了一下,发现问题是我在连接的两端使用了相同的字段名。我很惊讶这甚至是一个问题-或者我完全误解了这个问题。
下面是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; }

        // https://stackoverflow.com/a/7919012/8691687
        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

有谁能告诉我我在哪里犯了罪。
多谢了

aiazj4mn

aiazj4mn1#

我只是看看我在我的项目中是如何做到的。不幸的是,我无法描述为什么这是要走的路;- )

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();
}
b4lqfgs4

b4lqfgs42#

您根本不需要ID作为额外的属性。NHibernate足够聪明,在访问其Id属性时不会加载被引用对象。这是一个更干净的模型。

class TestZ
{
    public virtual TestA TestAObj { get; set; }
    public virtual TestB TestBObj { get; set; }
    public virtual TestC TestCObj { get; set; }
    public virtual string Name { get; set; }

    public override bool Equals(object obj)
    {
        var other = obj as TestZ;

        return other != null
            && A.Id == other.A.Id
            && B.Id == other.B.Id
            && C.Id == other.C.Id;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return A.Id ^ B.Id ^ C.Id;
        }
    }
}
class TestZMapping : ClassMap<TestZ>
    {
        public TestZMapping()
        {
            Table("test.table_z");
            CompositeId()
                .KeyRefernce(x => x.TestAObj, "a_id")
                .KeyRefernce(x => x.TestBObj, "b_id")
                .KeyRefernce(x => x.TestCObj, "c_id");

            Map(x => x.Name, "name");
        }
    }
lf5gs5x2

lf5gs5x23#

NHibernate 5强制您定义一个明确的更新数据库字段的责任-特别是当您有多个属性访问同一字段时。只允许一个属性Map是可写的-成为“新值”的所有者。
复合键并不真正定义类的属性-它只是定义了一个复合键。定义一个key-property并不允许你。对查询中的属性进行排序。因此,您必须在属性列表中重复此属性。但是这种重复必须是只读的,以给予key-property负责更新值。

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" />

NHibernate 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"/>
e0bqpujr

e0bqpujr4#

如果你需要同时使用ComposedId和ManyToOne作为同一个列,并得到错误:
“field list”中的未知列“SomeId”

列“SomeId”已添加到此SQL生成器中参数名称:columnName
然后将ManyToOne放入ComposedIdMap器中。像这样:

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"));
        }
    }

相关问题