以下のクラスは、私の実際のシナリオをレガシーデータベースで最小限の方法で表します。私はそれに新しい列を追加することができますが、これは私ができることです.310 + 100のテーブルデータベースはNHibernateに移植されない他の多くのレガシーアプリケーションで使用されているので(コンポジットキーからの移行はオプションではありません) :これは私がと3人の子供と1人の子供を持つ親を挿入するために使用しているコードがある関係の適切な逆(流暢なnhibernate)設定に関係なく、無関係の更新ステートメントを出力するNHibernate
public class MapeamentoParent : ClassMap<Parent>
{
public MapeamentoParent()
{
Id(_ => _.Id, "PARENT_ID").GeneratedBy.Identity();
HasMany(_ => _.Children)
.Inverse()
.AsSet()
.Cascade.All()
.KeyColumn("PARENT_ID");
}
}
public class MapeamentoChild : ClassMap<Child>
{
public MapeamentoChild()
{
CompositeId()
.KeyReference(_ => _.Parent, "PARENT_ID")
.KeyProperty(_ => _.ChildId, "CHILD_ID");
HasMany(_ => _.Items)
.AsSet()
.Cascade.All()
.KeyColumns.Add("PARENT_ID")
.KeyColumns.Add("CHILD_ID");
Version(Reveal.Member<Child>("version"));
}
}
public class MapeamentoItem : ClassMap<Item>
{
public MapeamentoItem()
{
Id(_ => _.ItemId).GeneratedBy.Assigned();
Version(Reveal.Member<Item>("version"));
}
}
:
public class Parent
{
public virtual long Id { get; protected set; }
ICollection<Child> children = new HashSet<Child>();
public virtual IEnumerable<Child> Children { get { return children; } }
public virtual void AddChildren(params Child[] children)
{
foreach (var child in children) AddChild(child);
}
public virtual Child AddChild(Child child)
{
child.Parent = this;
children.Add(child);
return child;
}
}
public class Child
{
public virtual Parent Parent { get; set; }
public virtual int ChildId { get; set; }
ICollection<Item> items = new HashSet<Item>();
public virtual ICollection<Item> Items { get { return items; } }
long version;
public override int GetHashCode()
{
return ChildId.GetHashCode()^(Parent != null ? Parent.Id.GetHashCode() : 0.GetHashCode());
}
public override bool Equals(object obj)
{
var c = obj as Child;
if (ReferenceEquals(c, null))
return false;
return ChildId == c.ChildId && Parent.Id == c.Parent.Id;
}
}
public class Item
{
public virtual long ItemId { get; set; }
long version;
}
これは、私が「既存の」データベースへのこれらのマッピングされてきた方法です品目:
using (var tx = session.BeginTransaction())
{
var parent = new Parent();
var child = new Child() { ChildId = 1, };
parent.AddChildren(
child,
new Child() { ChildId = 2, },
new Child() { ChildId = 3 });
child.Items.Add(new Item() { ItemId = 1 });
session.Save(parent);
tx.Commit();
}
これらは、前のコードのために生成されたSQL文である:
-- statement #1
INSERT INTO [Parent]
DEFAULT VALUES;
select SCOPE_IDENTITY()
-- statement #2
INSERT INTO [Child]
(version,
PARENT_ID,
CHILD_ID)
VALUES (1 /* @p0_0 */,
1 /* @p1_0 */,
1 /* @p2_0 */)
INSERT INTO [Child]
(version,
PARENT_ID,
CHILD_ID)
VALUES (1 /* @p0_1 */,
1 /* @p1_1 */,
2 /* @p2_1 */)
INSERT INTO [Child]
(version,
PARENT_ID,
CHILD_ID)
VALUES (1 /* @p0_2 */,
1 /* @p1_2 */,
3 /* @p2_2 */)
-- statement #3
INSERT INTO [Item]
(version,
ItemId)
VALUES (1 /* @p0_0 */,
1 /* @p1_0 */)
-- statement #4
UPDATE [Child]
SET version = 2 /* @p0 */
WHERE PARENT_ID = 1 /* @p1 */
AND CHILD_ID = 1 /* @p2 */
AND version = 1 /* @p3 */
-- statement #5
UPDATE [Child]
SET version = 2 /* @p0 */
WHERE PARENT_ID = 1 /* @p1 */
AND CHILD_ID = 2 /* @p2 */
AND version = 1 /* @p3 */
-- statement #6
UPDATE [Child]
SET version = 2 /* @p0 */
WHERE PARENT_ID = 1 /* @p1 */
AND CHILD_ID = 3 /* @p2 */
AND version = 1 /* @p3 */
-- statement #7
UPDATE [Item]
SET PARENT_ID = 1 /* @p0_0 */,
CHILD_ID = 1 /* @p1_0 */
WHERE ItemId = 1 /* @p2_0 */
文4、すべての情報が既に文でバッチ挿入にデータベースに送信されてから5及び6は、余分/外来である2
これは、親マッピングがHasMany(一対多)の関係でInverseプロパティを設定していない場合に予想される動作です。
我々はこのような子供の項目に1対多の関係を取り除く際に実際には、それはまだ見知らぬ人となっ:
は子供からコレクションを削除し、項目に子プロパティを追加します。
public class Child
{
public virtual Parent Parent { get; set; }
public virtual int ChildId { get; set; }
long version;
public override int GetHashCode()
{
return ChildId.GetHashCode()^(Parent != null ? Parent.Id.GetHashCode() : 0.GetHashCode());
}
public override bool Equals(object obj)
{
var c = obj as Child;
if (ReferenceEquals(c, null))
return false;
return ChildId == c.ChildId && Parent.Id == c.Parent.Id;
}
}
public class Item
{
public virtual Child Child { get; set; }
public virtual long ItemId { get; set; }
long version;
}
変更項目からのhasManyを削除し、戻って子供の項目の複合キーの参照を追加するために子どもと項目のマッピング:
public class MapeamentoChild : ClassMap<Child>
{
public MapeamentoChild()
{
CompositeId()
.KeyReference(_ => _.Parent, "PARENT_ID")
.KeyProperty(_ => _.ChildId, "CHILD_ID");
Version(Reveal.Member<Child>("version"));
}
}
public class MapeamentoItem : ClassMap<Item>
{
public MapeamentoItem()
{
Id(_ => _.ItemId).GeneratedBy.Assigned();
References(_ => _.Child).Columns("PARENT_ID", "CHILD_ID");
Version(Reveal.Member<Item>("version"));
}
}
変更C以下の(今私たちは、明示的にアイテムを保存呼び出す必要が予告)への頌歌:
using (var tx = session.BeginTransaction())
{
var parent = new Parent();
var child = new Child() { ChildId = 1, };
parent.AddChildren(
child,
new Child() { ChildId = 2, },
new Child() { ChildId = 3 });
var item = new Item() { ItemId = 1, Child = child };
session.Save(parent);
session.Save(item);
tx.Commit();
}
結果のSQL文は以下のとおりです。
-- statement #1
INSERT INTO [Parent]
DEFAULT VALUES;
select SCOPE_IDENTITY()
-- statement #2
INSERT INTO [Child]
(version,
PARENT_ID,
CHILD_ID)
VALUES (1 /* @p0_0 */,
1 /* @p1_0 */,
1 /* @p2_0 */)
INSERT INTO [Child]
(version,
PARENT_ID,
CHILD_ID)
VALUES (1 /* @p0_1 */,
1 /* @p1_1 */,
2 /* @p2_1 */)
INSERT INTO [Child]
(version,
PARENT_ID,
CHILD_ID)
VALUES (1 /* @p0_2 */,
1 /* @p1_2 */,
3 /* @p2_2 */)
-- statement #3
INSERT INTO [Item]
(version,
PARENT_ID,
CHILD_ID,
ItemId)
VALUES (1 /* @p0_0 */,
1 /* @p1_0 */,
1 /* @p2_0 */,
1 /* @p3_0 */)
あなたは無関係な/余分UPDATE文はありません見ることができるように、私はItemにChildへのリンクを持たせたくないので、オブジェクトモデルは自然にモデル化されていません。そして、私はChild内のItemsのコレクションが必要です。
ChildからHasManyリレーションシップを削除することを除いて、これらの不要/不要なUPDATEステートメントを防ぐ方法はありません。それは、子がすでに「倒立した」1対多の関係(それ自体を保存する責任がある)からの「多数」であるため、別のものから「1」の部分であるときに逆設定を尊重しないように見える多対の逆の関係...
これは私をナットにしています。私はこれらの余分なUPDATE文をよく考えて説明することなく受け入れることはできません:-)ここで何が起こっているのか知っていますか?
parent.AddChild(...)とchild.Parent = parentの間に違いがあることがわかりました。後で_not_を実行すると、不要なバージョンが更新されます(セッション中に子をParent.Childrenコレクションに追加することはありませんが、後のセッションで親がロードされている場合はそこにあります)。) – BatteryBackupUnit
@BatteryBackupUnitそれは本当ですが、ORMとしてコレクションに追加するときや親を直接設定したときに違うように誤解してはいけません。データベースからロードする前に、すぐにコレクションに置く必要があることがあります。そのような場合、これらの無関係のクエリはまだそこに存在します。しかし、私たちが望むのは、子どもを親に "結びつける"ことだけが望めば、私たちが "防ぐ"ことができることを知ってうれしいです...親のコレクションに追加するのではなく、child.Parentを使用してください。 :-) – Loudenvier
私は全く同意します。私はFluentNHibernate 'IHasManyConvention'に' .Not.OptimisticLock() 'を追加して、これがデフォルト動作であることを確認しました。 – BatteryBackupUnit