|
NHibernate - вопрос по ассоциации
#35978866
Ссылка:
Ссылка на сообщение:
Ссылка с названием темы:
|
|
|
|
Привет всем!
Есть две таблицы, связанные через ассоциацию:
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43.
CREATE TABLE tbl_foo
(
foo_id INT NOT NULL IDENTITY
,foo_payload VARCHAR( 100 ) NOT NULL
,CONSTRAINT PK__foo
PRIMARY KEY ( foo_id )
)
CREATE TABLE tbl_bar
(
bar_id INT NOT NULL IDENTITY
,bar_payload VARCHAR( 100 ) NOT NULL
,CONSTRAINT PK__bar
PRIMARY KEY ( bar_id )
)
CREATE TABLE tbl_foo_bar
(
foo_id INT NOT NULL
,bar_id INT NOT NULL
,foo_bar_payload VARCHAR( 100 ) NOT NULL
,CONSTRAINT PK__foo_bar
PRIMARY KEY ( foo_id, bar_id )
,CONSTRAINT FK__foo_bar__foo
FOREIGN KEY ( foo_id )
REFERENCES tbl_foo ( foo_id )
ON UPDATE CASCADE ON DELETE CASCADE
,CONSTRAINT FK__foo_bar__bar
FOREIGN KEY ( bar_id )
REFERENCES tbl_bar ( bar_id )
ON UPDATE CASCADE ON DELETE CASCADE
)
INSERT INTO tbl_foo ( foo_payload )
SELECT 'Foo 1'
UNION ALL SELECT 'Foo 2'
UNION ALL SELECT 'Foo 3'
INSERT INTO tbl_bar ( bar_payload )
SELECT 'Bar 1'
UNION ALL SELECT 'Bar 2'
UNION ALL SELECT 'Bar 3'
INSERT INTO tbl_foo_bar ( foo_id, bar_id, foo_bar_payload )
SELECT 1 , 1 , 'Foo 1 - Bar 1'
UNION ALL SELECT 1 , 2 , 'Foo 1 - Bar 2'
UNION ALL SELECT 1 , 3 , 'Foo 1 - Bar 3'
UNION ALL SELECT 2 , 2 , 'Foo 2 - Bar 2'
UNION ALL SELECT 2 , 3 , 'Foo 2 - Bar 3'
Ассоциация не имеет суррогатного ключа и добавлять его нельзя. Задача стоит построить такой маппинг, который бы позволил мне изменять значение foo_id или bar_id в таблице tbl_foo_bar, причем делать это с помощью операции UPDATE. Удалить запись и добавить другую не подходит, поскольку есть другие таблицы (здесь для краткости не показаны), которые ссылаются на tbl_foo_bar, и если запись удалится, то в этих связанных таблицах каскадно удалятся нужные данные.
Вот на какие классы делается отображение:
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144.
[Serializable]
public class Foo
{
public Foo()
{
}
#region Proprties
public virtual long Id { get; set; }
public virtual string Payload { get; set; }
public virtual ISet<FooBar> FooBars
{
get
{
return fooBars_;
}
set
{
fooBars_ = value;
}
}
private ISet<FooBar> fooBars_ = new HashedSet<FooBar>();
#endregion // Proprties
#region Equaltiy
public virtual bool Equals( Foo obj )
{
if ( ReferenceEquals( null, obj ) ) return false;
if ( ReferenceEquals( this, obj ) ) return true;
return Equals( obj.Payload, Payload );
}
public override bool Equals( object obj )
{
if ( ReferenceEquals( null, obj ) ) return false;
if ( ReferenceEquals( this, obj ) ) return true;
if ( obj.GetType() != typeof( Foo ) ) return false;
return Equals( (Foo)obj );
}
public override int GetHashCode()
{
return ( Payload != null ? Payload.GetHashCode() : 0 );
}
#endregion // Equaltiy
}
[Serializable]
public class Bar
{
#region Construction
public Bar()
{
}
#endregion // Construction
#region Proprties
public virtual long Id { get; set; }
public virtual string Payload { get; set; }
public virtual ISet<FooBar> FooBars
{
get
{
return fooBars_;
}
set
{
fooBars_ = value;
}
}
private ISet<FooBar> fooBars_ = new HashedSet<FooBar>();
#endregion // Proprties
#region Equaltiy
public virtual bool Equals( Bar obj )
{
if ( ReferenceEquals( null, obj ) ) return false;
if ( ReferenceEquals( this, obj ) ) return true;
return Equals( obj.Payload, Payload );
}
public override bool Equals( object obj )
{
if ( ReferenceEquals( null, obj ) ) return false;
if ( ReferenceEquals( this, obj ) ) return true;
if ( obj.GetType() != typeof( Bar ) ) return false;
return Equals( (Bar)obj );
}
public override int GetHashCode()
{
return ( Payload != null ? Payload.GetHashCode() : 0 );
}
#endregion // Equaltiy
}
public class FooBar
{
[Serializable]
public class Key
{
#region Construction
public Key()
{
}
public Key( Foo foo, Bar bar )
{
Foo = foo;
Bar = bar;
}
#endregion // Construction
#region Equality
public bool Equals( Key obj )
{
if ( ReferenceEquals( null, obj ) ) return false;
if ( ReferenceEquals( this, obj ) ) return true;
return Equals( obj.Foo, Foo ) && Equals( obj.Bar, Bar );
}
public override bool Equals( object obj )
{
if ( ReferenceEquals( null, obj ) ) return false;
if ( ReferenceEquals( this, obj ) ) return true;
return obj.GetType() == typeof( Key ) && Equals( (Key)obj );
}
public override int GetHashCode()
{
unchecked
{
return ( ( Foo != null ? Foo.GetHashCode() : 0 ) * 397 ) ^ ( Bar != null ? Bar.GetHashCode() : 0 );
}
}
#endregion // Equality
public Foo Foo { get; set; }
public Bar Bar { get; set; }
}
#region Construction
public FooBar()
{
}
#endregion // Construction
#region Proprties
public virtual Key Id { get; set; }
public virtual string Payload { get; set; }
#endregion // Proprties
}
А вот само отображение
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="ARR.Core.Domain.Test.Foo, ARR.Core" table="tbl_foo">
<id name="Id" column="foo_id" unsaved-value="0">
<generator class="identity" />
</id>
<property name="Payload" column="foo_payload"/>
<set name="FooBars" table="tbl_foo_bar">
<key column="foo_id"/>
<one-to-many class="ARR.Core.Domain.Test.FooBar, ARR.Core"/>
</set>
</class>
<class name="ARR.Core.Domain.Test.Bar, ARR.Core" table="tbl_bar">
<id name="Id" column="bar_id" unsaved-value="0">
<generator class="identity" />
</id>
<property name="Payload" column="bar_payload"/>
<set name="FooBars" table="tbl_foo_bar">
<key column="bar_id"/>
<one-to-many class="ARR.Core.Domain.Test.FooBar, ARR.Core"/>
</set>
</class>
<class name="ARR.Core.Domain.Test.FooBar, ARR.Core" table="tbl_foo_bar">
<composite-id name="Id" class="ARR.Core.Domain.Test.FooBar+Key, ARR.Core" >
<key-many-to-one name="Foo" column="foo_id" class="ARR.Core.Domain.Test.Foo, ARR.Core" />
<key-many-to-one name="Bar" column="bar_id" class="ARR.Core.Domain.Test.Bar, ARR.Core" />
</composite-id>
<property name="Payload" column="foo_bar_payload"/>
</class>
</hibernate-mapping>
Используется так
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13.
var session = NHibernateSessionManager.Instance.GetSession();
var foo = session.Get<Foo>( 2L );
var bar = session.Get<Bar>( 3L );
var newBar = session.Get<Bar>( 1L );
var key = new FooBar.Key( foo, bar );
var fooBar = session.Get<FooBar>( key );
// Modify association record
fooBar.Payload = "Modified Payload";
fooBar.Id.Bar = newBar;
session.Save( fooBar );
Это не работает, поскольку NHibernate не знает, что раньше ключ был другим. Поэтому пытается обновить запись по ключу (foo_id=2, bar_id=1), которой в базе нет. Вот запись из лога
1. 2. 3. 4. 5.
12 : 48 : 42 [ 9 ] DEBUG NHibernate.SQL -
UPDATE dbo.tbl_foo_bar
SET foo_bar_payload = @p0
WHERE foo_id = @p1 AND bar_id = @p2
;
@p0 = 'Modified Payload', @p1 = '2', @p2 = '1'
А я хотел бы увидеть такое
1. 2. 3. 4.
UPDATE dbo.tbl_foo_bar
SET foo_bar_payload = @p0, bar_id = @p1
WHERE foo_id = @p2 AND bar_id = @p3
;
@p0 = 'Modified Payload', @p1 = '1', @p2 = '2', @p3 = '3'
Спасибо.
|
|
|