4

私はかなり長い間この問題を理解しようとしました。私はそれを機能させるハックな方法を持っています。

これが Fluent NHibernate マッピングで可能かどうかを知りたいだけです。

たとえば、2 つのテーブルがあるとします。

Table ComissionLevel
{
    Year,
    ComissionLevelID,

    ... other properties ....
}
primary key (Year,ComissionLevelID)

Table ClientCommission
{
    Year,
    ClientID,
    CommissionLevelID_1,
    CommissionLevelID_2,

    ... other properties ...
}
primary key (Year,ClientID)
foreign key CommissionLevel1 (Year,CommissionLevelID_1)
foreign key CommissionLevel2 (Year,CommissionLevelID_2)

現在、私のマッピングは次のとおりです。

public ComissionLevelMap()
{
  Schema("XXXX");
  Table("ComissionLevel");
  LazyLoad();
  CompositeId()
    .KeyProperty(x => x.Year, set => {
        set.ColumnName("Year");
        set.Access.Property(); } )
    .KeyProperty(x => x.CommissionLevelID, set => {
        set.ColumnName("CommissionLevelID");
        set.Length(10);
        set.Access.Property(); } );

  HasMany<ClientCommission>(x => x.ClientCommissions)
    .Access.Property()
    .AsSet()
    .Cascade.AllDeleteOrphan()
    .LazyLoad()
    .Inverse()
    .Generic()
    .KeyColumns.Add("Year", mapping => mapping.Name("Year")
                                                         .SqlType("NUMBER")
                                                         .Nullable())
    .KeyColumns.Add("CommissionLevelID_1", mapping => mapping.Name("CommissionLevelID_1")
                                                         .SqlType("VARCHAR2")
                                                         .Nullable()
                                                         .Length(10));
  HasMany<ClientCommission>(x => x.ClientCommission2s)
    .Access.Property()
    .AsSet()
    .Cascade.AllDeleteOrphan()
    .LazyLoad()
    .Inverse()
    .Generic()
    .KeyColumns.Add("Year", mapping => mapping.Name("Year")
                                                         .SqlType("NUMBER")
                                                         .Nullable())
    .KeyColumns.Add("CommissionLevelID_2", mapping => mapping.Name("CommissionLevelID_2")
                                                         .SqlType("VARCHAR2")
                                                         .Nullable()
                                                         .Length(10));
}

public ClientCommissionMap()
{
  Schema("XXXXX");
  Table("ClientCommission");
  LazyLoad();
  CompositeId()
    .KeyProperty(x => x.ClientID, set => {
        set.ColumnName("ClientID");
        set.Length(10);
        set.Access.Property(); } )
    .KeyProperty(x => x.Year, set => {
        set.ColumnName("Year");
        set.Access.Property(); } );
  References(x => x.ComissionLevel1)
    .Class<ComissionLevel>()
    .Access.Property()
    .Cascade.None()
    .LazyLoad()
    .Insert()
    .Update()
    .Columns("Year", "CommissionLevelID_1");
  References(x => x.ComissionLevel2)
    .Class<ComissionLevel>()
    .Access.Property()
    .Cascade.None()
    .LazyLoad()
    .Insert()
    .Update()
    .Columns("Year", "CommissionLevelID_2");

}

私の問題は、CommissionLevel を作成してそのコレクションに ClientCommission を割り当てるたびに、session.save(CommissionLevel) を呼び出してそれらを保存すると、例外がスローされることです。

<Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index>.

ここでの私の質問は次のとおりです。

  1. NHibernate は関係を自動的に保存しますか? お気に入り:

        ClientCommission commission = new ClientCommission{Year = 2012, ClientID =SomeGuid};
        CommissionLevel  newCommissionLevel = new CommissionLevel{Year = 2012, CommissionLevelID =NewCommissionLevelGuid};
    
        newCommissionLevel.ClientCommission1s.Add(commission);
        newCommissionLevel.ClientCommission2s.Add(commission);
    
        CommissionLevelRepo.Save(newCommissionLevel);
    

    CommissionLevelRepo.Save(newCommissionLevel) を呼び出すと、NHibernate は ClientCommission.ComissionLevel1 と ClientCommission.ComissionLevel2 も更新する必要があります。

それとも言う必要がありますか

ClientCommission.ComissionLevel1 = newCommissionLevel; 
ClientCommission.ComissionLevel2 = newCommissionLevel; 
  1. 私が得た例外については、NHibernate が正しい列を生成しないためです。3 つの Year 列が生成されるようです。ComissionLevelID1 と CommissionLevelID2 という 2 つのプロパティを手動で作成すると、ClientCommission で .Insert() と .Update() を無効にすると、適切に保存されます。

これら2つのクラスをマッピングする適切な方法を教えてもらえますか?

どうもありがとう。

4

2 に答える 2

6

簡単な答え:複数の参照の列を共有することはできません

長い答え: NHibernateはすべての参照を互いに独立して扱いますが、挿入ステートメントの重複する列を排除するため、参照はもう存在しない列にアクセスしようとします。これは、共有列がオブジェクトモデルのto参照間で異なる場合、どちらが正しいかを判断できないためです。

データベーススキーマを変更してIDを一意にすることができる場合は、IDと参照で年をすべて無視します。

アップデート:

一部のマッピングを簡略化できます

CompositeId()
    .KeyProperty(x => x.Year, set => {
        set.ColumnName("Year");
        set.Access.Property(); } )
    .KeyProperty(x => x.CommissionLevelID, set => {
        set.ColumnName("CommissionLevelID");
        set.Length(10);
        set.Access.Property(); } );

// to
CompositeId()
    .KeyProperty(x => x.Year)  // columnname is equal propertyname by default
    .KeyProperty(x => x.CommissionLevelID, set => set.Length(10).Access.Property());  // property is default access and can also be left out


.SqlType("VARCHAR2").Length(10)
// to
.Length(10) or .SqlType("VARCHAR2")
// because length is ignored when sqltype is specified
于 2012-07-02T06:01:59.483 に答える
0

複合キーについては、Fluent NHibernate での複合キーのマッピングを参照してください。

マッピングを簡素化するために、主キーを 1 つのキーのみに変更し、それを表す一意のインデックスを作成できますが、これは最適なソリューションではありません。

前 (複合キー):

CREATE TABLE XPTO ( COD_XPTO1 INT NOT NULL IDENTITY,
                    COD_XPTO2 INT NOT NULL,
                    TXT_XPTO VARCHAR(10) NOT NULL)
ALTER TABLE XPTO
  ADD CONSTRAINT PK_XPTO (COD_XPTO1, COD_XPTO2)

後 (一意のインデックスを持つ単一のキー):

CREATE TABLE XPTO ( COD_XPTO1 INT NOT NULL IDENTITY,
                    COD_XPTO2 INT NOT NULL,
                    TXT_XPTO VARCHAR(10) NOT NULL)

ALTER TABLE XPTO
  ADD CONSTRAINT PK_XPTO (COD_XPTO1)

CREATE UNIQUE INDEX UK_XPTO ON XPTO (COD_XPTO1, COD_XPTO2)
于 2015-03-20T20:27:27.987 に答える