Skip to content

Commit 6218db3

Browse files
author
Mike Doerfler
committed
Added IdentifierBag to the NH Collections namespace.
SVN: trunk@497
1 parent 71ebf2c commit 6218db3

File tree

6 files changed

+429
-6
lines changed

6 files changed

+429
-6
lines changed

src/NHibernate/Cfg/Binder.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,18 @@ public override Mapping.Collection Create(XmlNode node, string prefix, Persisten
13081308

13091309
}
13101310

1311-
//TODO: H2.0.3: IDBAG is missing
1311+
private static CollectionType IDBAG = new CollectionTypeBag("idbag");
1312+
private class CollectionTypeIdBag : CollectionType
1313+
{
1314+
public CollectionTypeIdBag(string xmlTag) : base(xmlTag) {}
1315+
public override Mapping.Collection Create(XmlNode node, string prefix, PersistentClass owner, Mappings mappings)
1316+
{
1317+
IdentifierBag bag = new IdentifierBag(owner);
1318+
Binder.BindCollection(node, bag, prefix, mappings);
1319+
return bag;
1320+
}
1321+
1322+
}
13121323

13131324
private static CollectionType ARRAY = new CollectionTypeArray("array");
13141325
private class CollectionTypeArray : CollectionType
@@ -1339,6 +1350,7 @@ static CollectionType()
13391350
{
13401351
Instances.Add(MAP.ToString(), MAP);
13411352
Instances.Add(BAG.ToString(), BAG);
1353+
Instances.Add(IDBAG.ToString(), IDBAG);
13421354
Instances.Add(SET.ToString(), SET);
13431355
Instances.Add(LIST.ToString(), LIST);
13441356
Instances.Add(ARRAY.ToString(), ARRAY);
+358
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
using System;
2+
using System.Collections;
3+
using System.Data;
4+
5+
using NHibernate.Engine;
6+
using NHibernate.Type;
7+
8+
namespace NHibernate.Collection
9+
{
10+
/// <summary>
11+
/// An <c>IdentiferBag</c> implements "bag" semantics more efficiently than
12+
/// a regular <see cref="Bag" /> by adding a synthetic identifier column to the
13+
/// table.
14+
/// </summary>
15+
/// <remarks>
16+
/// <para>
17+
/// The identifier is unique for all rows in the table, allowing very efficient
18+
/// updates and deletes. The value of the identifier is never exposed to the
19+
/// application.
20+
/// </para>
21+
/// <para>
22+
/// <c>IdentifierBag</c>s may not be used for a many-to-one association. Furthermore,
23+
/// there is no reason to use <c>inverse="true"</c>.
24+
/// </para>
25+
/// </remarks>
26+
public class IdentifierBag : ODMGCollection, IList
27+
{
28+
private IList values;
29+
private IList valuesIdentifiers;
30+
31+
private IDictionary identifiers; //element -> id
32+
33+
public IdentifierBag(ISessionImplementor session) : base(session)
34+
{
35+
}
36+
37+
public IdentifierBag(ISessionImplementor session, ICollection coll) : base(session)
38+
{
39+
if(coll is IList)
40+
{
41+
values = (IList)coll;
42+
}
43+
else
44+
{
45+
values = new ArrayList();
46+
foreach(object obj in coll)
47+
{
48+
values.Add(obj);
49+
}
50+
}
51+
52+
initialized = true;
53+
directlyAccessible = true;
54+
identifiers = new Hashtable();
55+
}
56+
57+
public IdentifierBag(ISessionImplementor session, CollectionPersister persister, object disassembled, object owner) : base(session)
58+
{
59+
BeforeInitialize(persister);
60+
object[] array = (object[])disassembled;
61+
62+
for(int i=0; i < array.Length; i+=2)
63+
{
64+
object obj = persister.ElementType.Assemble( array[i+1], session, owner );
65+
identifiers[obj] = persister.IdentifierType.Assemble( array[i], session, owner );
66+
values.Add(obj);
67+
}
68+
69+
initialized = true;
70+
}
71+
72+
73+
#region IList Members
74+
75+
public bool IsReadOnly
76+
{
77+
get { return false; }
78+
}
79+
80+
public object this[int index]
81+
{
82+
get
83+
{
84+
Read();
85+
return values[index];
86+
}
87+
set
88+
{
89+
Write();
90+
values[index] = value;
91+
}
92+
}
93+
94+
public void RemoveAt(int index)
95+
{
96+
Write();
97+
values.RemoveAt(index);
98+
}
99+
100+
public void Insert(int index, object value)
101+
{
102+
Write();
103+
values.Insert(index, value);
104+
}
105+
106+
public void Remove(object value)
107+
{
108+
Write();
109+
values.Remove(value);
110+
}
111+
112+
public bool Contains(object value)
113+
{
114+
Read();
115+
return values.Contains(value);
116+
}
117+
118+
public void Clear()
119+
{
120+
Write();
121+
values.Clear();
122+
identifiers.Clear();
123+
}
124+
125+
public int IndexOf(object value)
126+
{
127+
Read();
128+
return values.IndexOf(value);
129+
}
130+
131+
public int Add(object value)
132+
{
133+
Write();
134+
return values.Add(value);
135+
}
136+
137+
public bool IsFixedSize
138+
{
139+
get { return false; }
140+
}
141+
142+
#endregion
143+
144+
#region ICollection Members
145+
146+
public override bool IsSynchronized
147+
{
148+
get { return false; }
149+
}
150+
151+
public override int Count
152+
{
153+
get
154+
{
155+
Read();
156+
return values.Count;
157+
}
158+
}
159+
160+
public override void CopyTo(Array array, int index)
161+
{
162+
Read();
163+
values.CopyTo(array, index);
164+
}
165+
166+
public override object SyncRoot
167+
{
168+
get { return values.SyncRoot; }
169+
}
170+
171+
#endregion
172+
173+
#region IEnumerable Members
174+
175+
public override IEnumerator GetEnumerator()
176+
{
177+
return values.GetEnumerator();
178+
}
179+
180+
#endregion
181+
182+
public override void BeforeInitialize(CollectionPersister persister)
183+
{
184+
identifiers = new Hashtable();
185+
values = new ArrayList();
186+
valuesIdentifiers = new ArrayList();
187+
}
188+
189+
public override object Disassemble(CollectionPersister persister)
190+
{
191+
object[] result = new object[values.Count * 2];
192+
193+
int i = 0;
194+
foreach(object obj in values)
195+
{
196+
result[i++] = persister.IdentifierType.Disassemble( identifiers[obj], session );
197+
result[i++] = persister.ElementType.Disassemble( obj, session );
198+
}
199+
200+
return result;
201+
}
202+
203+
public override ICollection Elements()
204+
{
205+
return values;
206+
}
207+
208+
public override bool Empty
209+
{
210+
get
211+
{
212+
return (values.Count == 0);
213+
}
214+
}
215+
216+
public override void EndRead(CollectionPersister persister, object owner)
217+
{
218+
for(int i = 0 ;i < valuesIdentifiers.Count; i++)
219+
{
220+
object element = persister.ElementType.ResolveIdentifier(valuesIdentifiers[i], session, owner);
221+
values[i] = element;
222+
}
223+
}
224+
225+
[Obsolete("See PersistentCollection.ReadEntries for reason")]
226+
public override void ReadEntries(ICollection entries)
227+
{
228+
throw new NotSupportedException("Should not call...");
229+
}
230+
231+
public override ICollection Entries()
232+
{
233+
return values;
234+
}
235+
236+
public override bool EntryExists(object entry, int i)
237+
{
238+
return entry!=null;
239+
}
240+
241+
public override bool EqualsSnapshot(IType elementType)
242+
{
243+
IDictionary snap = (IDictionary)GetSnapshot();
244+
if(snap.Count!=values.Count) return false;
245+
246+
int i = 0;
247+
foreach(object obj in values)
248+
{
249+
object id = identifiers[i++];
250+
if(id==null) return false;
251+
252+
object old = snap[id];
253+
if( elementType.IsDirty( old, obj, session ) ) return false;
254+
}
255+
256+
return true;
257+
258+
}
259+
260+
261+
public override ICollection GetDeletes(IType elemType)
262+
{
263+
IDictionary snap = (IDictionary) GetSnapshot();
264+
IList deletes = new ArrayList( snap.Keys );
265+
266+
int i = 0;
267+
foreach(object obj in values)
268+
{
269+
if( obj!=null ) deletes.Remove( identifiers[i++] );
270+
}
271+
272+
return deletes;
273+
}
274+
275+
public override object GetIndex(object entry, int i)
276+
{
277+
return new NotImplementedException("Bags don't have indexes");
278+
}
279+
280+
public override bool NeedsInserting(object entry, int i, IType elemType)
281+
{
282+
IDictionary snap = (IDictionary)GetSnapshot();
283+
object id = identifiers[i];
284+
285+
return entry!=null && ( id==null || snap[id]==null );
286+
}
287+
288+
public override bool NeedsUpdating(object entry, int i, IType elemType)
289+
{
290+
if(entry==null) return false;
291+
IDictionary snap = (IDictionary)GetSnapshot();
292+
293+
object id = identifiers[i];
294+
if(id==null) return false;
295+
296+
object old = snap[id];
297+
return entry!=null && old!=null && elemType.IsDirty(old, entry, session);
298+
}
299+
300+
public override object ReadFrom(IDataReader reader, CollectionPersister persister, object owner)
301+
{
302+
object elementIdentifier = persister.ReadElementIdentifier(reader, owner, session);
303+
values.Add(null);
304+
valuesIdentifiers.Add(elementIdentifier);
305+
306+
identifiers[values.Count - 1] = persister.ReadIdentifier(reader, session);
307+
308+
return elementIdentifier;
309+
}
310+
311+
protected override object Snapshot(CollectionPersister persister)
312+
{
313+
IDictionary map = new Hashtable(values.Count);
314+
315+
int i = 0;
316+
foreach(object obj in values)
317+
{
318+
object key = identifiers[i++];
319+
map[key] = persister.ElementType.DeepCopy(obj);
320+
}
321+
322+
return map;
323+
}
324+
325+
public override ICollection GetOrphans(object snapshot)
326+
{
327+
IDictionary sn = (IDictionary)GetSnapshot();
328+
ArrayList result = new ArrayList();
329+
result.AddRange(sn.Values);
330+
PersistentCollection.IdentityRemoveAll(result, values, session);
331+
return result;
332+
}
333+
334+
public override void PreInsert(CollectionPersister persister, object entry, int i)
335+
{
336+
try
337+
{
338+
object id = persister.IdentifierGenerator.Generate(session, entry);
339+
// TODO: native ids
340+
identifiers[i] = id;
341+
}
342+
catch (Exception sqle)
343+
{
344+
throw new ADOException("Could not generate collection row id.", sqle);
345+
}
346+
}
347+
348+
public override void WriteTo(IDbCommand st, CollectionPersister persister, object entry, int i, bool writeOrder)
349+
{
350+
persister.WriteElement(st, entry, writeOrder, session);
351+
persister.WriteIdentifier(st, identifiers[i], writeOrder, session);
352+
}
353+
354+
355+
356+
357+
}
358+
}

0 commit comments

Comments
 (0)