Skip to content

Commit f4c2764

Browse files
Merge pull request #627 from fredericDelaporte/NH-2176
NH-4011 - transaction scope failures
2 parents 8d0f102 + 75d2c9f commit f4c2764

File tree

104 files changed

+7430
-2090
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+7430
-2090
lines changed

doc/reference/modules/batch.xml

Lines changed: 78 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
look like this:
77
</para>
88

9-
<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
10-
ITransaction tx = session.BeginTransaction();
11-
for ( int i=0; i<100000; i++ ) {
12-
Customer customer = new Customer(.....);
13-
session.Save(customer);
14-
}
15-
tx.Commit();
16-
session.Close();]]></programlisting>
9+
<programlisting><![CDATA[using (ISession session = sessionFactory.OpenSession())
10+
using (ITransaction tx = session.BeginTransaction())
11+
{
12+
for (int i = 0; i < 100000; i++)
13+
{
14+
Customer customer = new Customer(.....);
15+
session.Save(customer);
16+
}
17+
tx.Commit();
18+
}]]></programlisting>
1719

1820
<para>
1921
This would fall over with an <literal>OutOfMemoryException</literal> somewhere
@@ -56,21 +58,24 @@ session.Close();]]></programlisting>
5658
the first-level cache.
5759
</para>
5860

59-
<programlisting><![CDATA[ISession session = sessionFactory.openSession();
60-
ITransaction tx = session.BeginTransaction();
61-
62-
for ( int i=0; i<100000; i++ ) {
63-
Customer customer = new Customer(.....);
64-
session.Save(customer);
65-
if ( i % 20 == 0 ) { //20, same as the ADO batch size
66-
//flush a batch of inserts and release memory:
67-
session.Flush();
68-
session.Clear();
61+
<programlisting><![CDATA[using (ISession session = sessionFactory.openSession())
62+
using (ITransaction tx = session.BeginTransaction())
63+
{
64+
for (int i = 0; i < 100000; i++)
65+
{
66+
Customer customer = new Customer(.....);
67+
session.Save(customer);
68+
// 20, same as the ADO batch size
69+
if (i % 20 == 0)
70+
{
71+
// flush a batch of inserts and release memory:
72+
session.Flush();
73+
session.Clear();
74+
}
6975
}
70-
}
71-
72-
tx.Commit();
73-
session.Close();]]></programlisting>
76+
77+
tx.Commit();
78+
}]]></programlisting>
7479

7580
</sect1>
7681

@@ -90,20 +95,21 @@ session.Close();]]></programlisting>
9095
to data aliasing effects, due to the lack of a first-level cache. A stateless
9196
session is a lower-level abstraction, much closer to the underlying ADO.
9297
</para>
93-
94-
<programlisting><![CDATA[IStatelessSession session = sessionFactory.OpenStatelessSession();
95-
ITransaction tx = session.BeginTransaction();
96-
97-
var customers = session.GetNamedQuery("GetCustomers")
98-
.Enumerable<Customer>();
99-
while ( customers.MoveNext() ) {
100-
Customer customer = customers.Current;
101-
customer.updateStuff(...);
102-
session.Update(customer);
103-
}
104-
105-
tx.Commit();
106-
session.Close();]]></programlisting>
98+
99+
<programlisting><![CDATA[using (IStatelessSession session = sessionFactory.OpenStatelessSession())
100+
using (ITransaction tx = session.BeginTransaction())
101+
{
102+
var customers = session.GetNamedQuery("GetCustomers")
103+
.Enumerable<Customer>();
104+
while (customers.MoveNext())
105+
{
106+
Customer customer = customers.Current;
107+
customer.updateStuff(...);
108+
session.Update(customer);
109+
}
110+
111+
tx.Commit();
112+
}]]></programlisting>
107113

108114
<para>
109115
Note that in this code example, the <literal>Customer</literal> instances returned
@@ -176,17 +182,17 @@ session.Close();]]></programlisting>
176182
<literal>IQuery.ExecuteUpdate()</literal> method:
177183
</para>
178184

179-
<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
180-
ITransaction tx = session.BeginTransaction();
181-
182-
string hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
183-
// or string hqlUpdate = "update Customer set name = :newName where name = :oldName";
184-
int updatedEntities = s.CreateQuery( hqlUpdate )
185-
.SetString( "newName", newName )
186-
.SetString( "oldName", oldName )
185+
<programlisting><![CDATA[using (ISession session = sessionFactory.OpenSession())
186+
using (ITransaction tx = session.BeginTransaction())
187+
{
188+
string hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
189+
// or string hqlUpdate = "update Customer set name = :newName where name = :oldName";
190+
int updatedEntities = s.CreateQuery(hqlUpdate)
191+
.SetString("newName", newName)
192+
.SetString("oldName", oldName)
187193
.ExecuteUpdate();
188-
tx.Commit();
189-
session.Close();]]></programlisting>
194+
tx.Commit();
195+
}]]></programlisting>
190196

191197
<para>
192198
HQL <literal>UPDATE</literal> statements, by default do not effect the
@@ -198,15 +204,17 @@ session.Close();]]></programlisting>
198204
This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
199205
keyword.
200206
</para>
201-
<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
202-
ITransaction tx = session.BeginTransaction();
203-
string hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
204-
int updatedEntities = s.CreateQuery( hqlUpdate )
205-
.SetString( "newName", newName )
206-
.SetString( "oldName", oldName )
207+
208+
<programlisting><![CDATA[using (ISession session = sessionFactory.OpenSession())
209+
using (ITransaction tx = session.BeginTransaction())
210+
{
211+
string hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
212+
int updatedEntities = s.CreateQuery(hqlUpdate)
213+
.SetString("newName", newName)
214+
.SetString("oldName", oldName)
207215
.ExecuteUpdate();
208-
tx.Commit();
209-
session.Close();]]></programlisting>
216+
tx.Commit();
217+
}]]></programlisting>
210218

211219
<para>
212220
Note that custom version types (<literal>NHibernate.Usertype.IUserVersionType</literal>)
@@ -218,16 +226,16 @@ session.Close();]]></programlisting>
218226
method:
219227
</para>
220228

221-
<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
222-
ITransaction tx = session.BeginTransaction();
223-
224-
String hqlDelete = "delete Customer c where c.name = :oldName";
225-
// or String hqlDelete = "delete Customer where name = :oldName";
226-
int deletedEntities = s.CreateQuery( hqlDelete )
227-
.SetString( "oldName", oldName )
229+
<programlisting><![CDATA[using (ISession session = sessionFactory.OpenSession())
230+
using (ITransaction tx = session.BeginTransaction())
231+
{
232+
string hqlDelete = "delete Customer c where c.name = :oldName";
233+
// or String hqlDelete = "delete Customer where name = :oldName";
234+
int deletedEntities = s.CreateQuery(hqlDelete)
235+
.SetString("oldName", oldName)
228236
.ExecuteUpdate();
229-
tx.Commit();
230-
session.Close();]]></programlisting>
237+
tx.Commit();
238+
}]]></programlisting>
231239

232240
<para>
233241
The <literal>int</literal> value returned by the <literal>IQuery.ExecuteUpdate()</literal>
@@ -302,14 +310,14 @@ session.Close();]]></programlisting>
302310
An example HQL <literal>INSERT</literal> statement execution:
303311
</para>
304312

305-
<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
306-
ITransaction tx = session.BeginTransaction();
307-
308-
var hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
309-
int createdEntities = s.CreateQuery( hqlInsert )
313+
<programlisting><![CDATA[using (ISession session = sessionFactory.OpenSession())
314+
using (ITransaction tx = session.BeginTransaction())
315+
{
316+
var hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
317+
int createdEntities = s.CreateQuery(hqlInsert)
310318
.ExecuteUpdate();
311-
tx.Commit();
312-
session.Close();]]></programlisting>
319+
tx.Commit();
320+
}]]></programlisting>
313321

314322
</sect1>
315323

doc/reference/modules/collection_mapping.xml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -622,12 +622,14 @@ HashedSet hs = (HashedSet) cat.Kittens; //Error!]]></programlisting>
622622
However, if the application tries something like this:
623623
</para>
624624

625-
<programlisting><![CDATA[s = sessions.OpenSession();
626-
ITransaction tx = sessions.BeginTransaction();
627-
User u = (User) s.Find("from User u where u.Name=?", userName, NHibernateUtil.String)[0];
628-
IDictionary permissions = u.Permissions;
629-
tx.Commit();
630-
s.Close();
625+
<programlisting><![CDATA[IDictionary permissions;
626+
using (s = sessions.OpenSession())
627+
using (ITransaction tx = sessions.BeginTransaction())
628+
{
629+
User u = (User) s.Find("from User u where u.Name=?", userName, NHibernateUtil.String)[0];
630+
permissions = u.Permissions;
631+
tx.Commit();
632+
}
631633
632634
int accessLevel = (int) permissions["accounts"]; // Error!]]></programlisting>
633635

doc/reference/modules/configuration.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ ISession session = sessions.OpenSession(conn);
600600
</entry>
601601
<entry>
602602
The class name of a custom <literal>ITransactionFactory</literal> implementation,
603-
defaults to the built-in <literal>AdoNetWithDistributedTransactionFactory</literal>.
603+
defaults to the built-in <literal>AdoNetWithSystemTransactionFactory</literal>.
604604
<para>
605605
<emphasis role="strong">eg.</emphasis>
606606
<literal>classname.of.TransactionFactory, assembly</literal>

doc/reference/modules/manipulating_data.xml

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -780,15 +780,18 @@ sess.Lock(pk, LockMode.Upgrade);]]></programlisting>
780780
</para>
781781

782782
<programlisting><![CDATA[sess = sf.OpenSession();
783-
ITransaction tx = sess.BeginTransaction();
784-
sess.FlushMode = FlushMode.Commit; //allow queries to return stale state
785-
Cat izi = (Cat) sess.Load(typeof(Cat), id);
786-
izi.Name = "iznizi";
787-
// execute some queries....
788-
sess.Find("from Cat as cat left outer join cat.Kittens kitten");
789-
//change to izi is not flushed!
790-
...
791-
tx.Commit(); //flush occurs]]></programlisting>
783+
using (ITransaction tx = sess.BeginTransaction())
784+
{
785+
// allow queries to return stale state
786+
sess.FlushMode = FlushMode.Commit;
787+
Cat izi = (Cat) sess.Load(typeof(Cat), id);
788+
izi.Name = "iznizi";
789+
// execute some queries....
790+
sess.Find("from Cat as cat left outer join cat.Kittens kitten");
791+
// change to izi is not flushed!
792+
...
793+
tx.Commit(); // flush occurs
794+
}]]></programlisting>
792795

793796
</sect1>
794797

doc/reference/modules/performance.xml

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -132,33 +132,25 @@
132132
fetching for single-valued associations. These defaults make sense for almost
133133
all associations in almost all applications.
134134
</para>
135-
136-
<!--
137-
<para>
138-
<emphasis>Note:</emphasis> if you set
139-
<literal>hibernate.default_batch_fetch_size</literal>, NHibernate will use the
140-
batch fetch optimization for lazy fetching (this optimization may also be enabled
141-
at a more granular level).
142-
</para>
143-
-->
144-
135+
145136
<para>
146137
However, lazy fetching poses one problem that you must be aware of. Access to a
147138
lazy association outside of the context of an open NHibernate session will result
148139
in an exception. For example:
149140
</para>
150-
151-
<programlisting><![CDATA[s = sessions.OpenSession();
152-
Transaction tx = s.BeginTransaction();
153-
154-
User u = (User) s.CreateQuery("from User u where u.Name=:userName")
155-
.SetString("userName", userName).UniqueResult();
156-
IDictionary permissions = u.Permissions;
157141

158-
tx.Commit();
159-
s.Close();
142+
<programlisting><![CDATA[IDictionary permissions;
143+
using (var s = sessions.OpenSession())
144+
using (Transaction tx = s.BeginTransaction())
145+
{
146+
User u = (User)s.CreateQuery("from User u where u.Name=:userName")
147+
.SetString("userName", userName).UniqueResult();
148+
IDictionary permissions = u.Permissions;
149+
150+
tx.Commit();
151+
}
160152
161-
int accessLevel = (int) permissions["accounts"]; // Error!]]></programlisting>
153+
int accessLevel = (int)permissions["accounts"]; // Error!]]></programlisting>
162154

163155
<para>
164156
Since the <literal>permissions</literal> collection was not initialized
@@ -262,8 +254,9 @@ int accessLevel = (int) permissions["accounts"]; // Error!]]></programlisting>
262254
</para>
263255

264256
<para>
265-
A completely different way to avoid problems with N+1 selects is to use the
266-
second-level cache.
257+
A completely different way to avoid problems with N+1 selects is to use the
258+
<link linkend="performance-cache">second-level cache</link>, or to enable
259+
<link linkend="performance-fetching-batch">batch fetching</link>.
267260
</para>
268261

269262
</sect2>
@@ -310,20 +303,24 @@ int accessLevel = (int) permissions["accounts"]; // Error!]]></programlisting>
310303
instance of <literal>DomesticCat</literal>:
311304
</para>
312305

313-
<programlisting><![CDATA[Cat cat = (Cat) session.Load(typeof(Cat), id); // instantiate a proxy (does not hit the db)
314-
if ( cat.IsDomesticCat ) { // hit the db to initialize the proxy
315-
DomesticCat dc = (DomesticCat) cat; // Error!
306+
<programlisting><![CDATA[// instantiate a proxy (does not hit the db)
307+
Cat cat = (Cat) session.Load(typeof(Cat), id);
308+
// hit the db to initialize the proxy
309+
if ( cat.IsDomesticCat ) {
310+
DomesticCat dc = (DomesticCat) cat; // Error!
316311
....
317312
}]]></programlisting>
318313

319314
<para>
320315
Secondly, it is possible to break proxy <literal>==</literal>.
321316
</para>
322317

323-
<programlisting><![CDATA[Cat cat = (Cat) session.Load(typeof(Cat), id); // instantiate a Cat proxy
324-
DomesticCat dc =
325-
(DomesticCat) session.Load(typeof(DomesticCat), id); // acquire new DomesticCat proxy!
326-
System.out.println(cat==dc); // false]]></programlisting>
318+
<programlisting><![CDATA[// instantiate a Cat proxy
319+
Cat cat = (Cat)session.Load(typeof(Cat), id);
320+
DomesticCat dc =
321+
// acquire new DomesticCat proxy!
322+
(DomesticCat)session.Load(typeof(DomesticCat), id);
323+
Console.WriteLine(cat == dc); // false]]></programlisting>
327324

328325
<para>
329326
However, the situation is not quite as bad as it looks. Even though we now have two references
@@ -561,6 +558,12 @@ ICat fritz = (ICat) iter.Current;]]></programlisting>
561558
<emphasis>materialized path</emphasis> might be a better option for read-mostly trees.)
562559
</para>
563560

561+
<para>
562+
<emphasis>Note:</emphasis> if you set <literal>default_batch_fetch_size</literal>
563+
in configuration, NHibernate will configure the batch fetch optimization for lazy fetching
564+
globally. Batch sizes specified at more granular level take precedence.
565+
</para>
566+
564567
</sect2>
565568

566569
<sect2 id="performance-fetching-subselect">

doc/reference/modules/query_criteria.xml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,15 +294,16 @@ IList results = session.CreateCriteria(typeof(Cat))
294294
The <literal>DetachedCriteria</literal> class lets you create a query outside the scope
295295
of a session, and then later execute it using some arbitrary <literal>ISession</literal>.
296296
</para>
297-
297+
298298
<programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.For(typeof(Cat))
299299
.Add( Expression.Eq("sex", 'F') );
300-
301-
ISession session = ....;
302-
ITransaction txn = session.BeginTransaction();
303-
IList results = query.GetExecutableCriteria(session).SetMaxResults(100).List();
304-
txn.Commit();
305-
session.Close();]]></programlisting>
300+
301+
using (ISession session = ....)
302+
using (ITransaction txn = session.BeginTransaction())
303+
{
304+
IList results = query.GetExecutableCriteria(session).SetMaxResults(100).List();
305+
txn.Commit();
306+
}]]></programlisting>
306307

307308
<para>
308309
A <literal>DetachedCriteria</literal> may also be used to express a sub-query. ICriterion

0 commit comments

Comments
 (0)