forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathperformance.xml
1492 lines (1270 loc) · 67 KB
/
performance.xml
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<chapter id="performance">
<title>Improving performance</title>
<sect1 id="performance-fetching" revision="2">
<title>Fetching strategies</title>
<para>
A <emphasis>fetching strategy</emphasis> is the strategy NHibernate will use for
retrieving associated objects if the application needs to navigate the association.
Fetch strategies may be declared in the O/R mapping metadata, or overridden by a
particular HQL or <literal>Criteria</literal> query.
</para>
<para>
NHibernate defines the following fetching strategies:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Join fetching</emphasis> - NHibernate retrieves the
associated instance or collection in the same <literal>SELECT</literal>,
using an <literal>OUTER JOIN</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Select fetching</emphasis> - a second <literal>SELECT</literal>
is used to retrieve the associated entity or collection. Unless
you explicitly disable lazy fetching by specifying <literal>lazy="false"</literal>,
this second select will only be executed when you actually access the
association.
</para>
</listitem>
<listitem>
<para>
<emphasis>Subselect fetching</emphasis> - a second <literal>SELECT</literal>
is used to retrieve the associated collections for all entities retrieved in a
previous query or fetch. Unless you explicitly disable lazy fetching by specifying
<literal>lazy="false"</literal>, this second select will only be executed when you
actually access the association.
</para>
</listitem>
<listitem>
<para>
<emphasis>"Extra-lazy" collection fetching</emphasis> - individual
elements of the collection are accessed from the database as needed.
NHibernate tries not to fetch the whole collection into memory unless
absolutely needed (suitable for very large collections)
</para>
</listitem>
<listitem>
<para>
<emphasis>Batch fetching</emphasis> - an optimization strategy
for select fetching - NHibernate retrieves a batch of entity instances
or collections in a single <literal>SELECT</literal>, by specifying
a list of primary keys or foreign keys.
</para>
</listitem>
</itemizedlist>
<para>
NHibernate also distinguishes between:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Immediate fetching</emphasis> - an association, collection or
attribute is fetched immediately, when the owner is loaded.
</para>
</listitem>
<listitem>
<para>
<emphasis>Lazy collection fetching</emphasis> - a collection is fetched
when the application invokes an operation upon that collection. (This
is the default for collections.)
</para>
</listitem>
<!--
<listitem>
<para>
<emphasis>"Extra-lazy" collection fetching</emphasis> - individual
elements of the collection are accessed from the database as needed.
NHibernate tries not to fetch the whole collection into memory unless
absolutely needed (suitable for very large collections)
</para>
</listitem>
-->
<listitem>
<para>
<emphasis>Proxy fetching</emphasis> - a single-valued association is
fetched when a method other than the identifier getter is invoked
upon the associated object.
</para>
</listitem>
<!--
<listitem>
<para>
<emphasis>"No-proxy" fetching</emphasis> - a single-valued association is
fetched when the instance variable is accessed. Compared to proxy fetching,
this approach is less lazy (the association is fetched even when only the
identifier is accessed) but more transparent, since no proxy is visible to
the application. This approach requires build-time bytecode instrumentation
and is rarely necessary.
</para>
</listitem>
<listitem>
<para>
<emphasis>Lazy attribute fetching</emphasis> - an attribute or single
valued association is fetched when the instance variable is accessed.
This approach requires build-time bytecode instrumentation and is rarely
necessary.
</para>
</listitem>
-->
</itemizedlist>
<para>
We have two orthogonal notions here: <emphasis>when</emphasis> is the association
fetched, and <emphasis>how</emphasis> is it fetched (what SQL is used). Don't
confuse them! We use <literal>fetch</literal> to tune performance. We may use
<literal>lazy</literal> to define a contract for what data is always available
in any detached instance of a particular class.
</para>
<sect2 id="performance-fetching-lazy">
<title>Working with lazy associations</title>
<para>
By default, NHibernate uses lazy select fetching for collections and lazy proxy
fetching for single-valued associations. These defaults make sense for almost
all associations in almost all applications.
</para>
<para>
However, lazy fetching poses one problem that you must be aware of. Access to a
lazy association outside of the context of an open NHibernate session will result
in an exception. For example:
</para>
<programlisting><![CDATA[IDictionary<string, int> permissions;
using (var s = sessions.OpenSession())
using (Transaction tx = s.BeginTransaction())
{
User u = s.CreateQuery("from User u where u.Name=:userName")
.SetString("userName", userName).UniqueResult<User>();
permissions = u.Permissions;
tx.Commit();
}
int accessLevel = permissions["accounts"]; // Error!]]></programlisting>
<para>
Since the <literal>permissions</literal> collection was not initialized
when the <literal>ISession</literal> was closed, the collection will not
be able to load its state. <emphasis>NHibernate does not support lazy
initialization for detached objects</emphasis>. The fix is to move the
code that reads from the collection to just before the transaction
is committed.
</para>
<para>
Alternatively, we could use a non-lazy collection or association,
by specifying <literal>lazy="false"</literal> for the association mapping.
However, it is intended that lazy initialization be used for almost all
collections and associations. If you define too many non-lazy associations
in your object model, NHibernate will end up needing to fetch the entire
database into memory in every transaction!
</para>
<para>
On the other hand, we often want to choose join fetching (which is non-lazy by
nature) instead of select fetching in a particular transaction. We'll now see
how to customize the fetching strategy. In NHibernate, the mechanisms for
choosing a fetch strategy are identical for single-valued associations and
collections.
</para>
</sect2>
<sect2 id="performance-fetching-custom" revision="4">
<title>Tuning fetch strategies</title>
<para>
Select fetching (the default) is extremely vulnerable to N+1 selects problems,
so we might want to enable join fetching in the mapping document:
</para>
<programlisting><![CDATA[<set name="Permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set]]></programlisting>
<programlisting><![CDATA[<many-to-one name="Mother" class="Cat" fetch="join"/>]]></programlisting>
<para>
The <literal>fetch</literal> strategy defined in the mapping document affects:
</para>
<itemizedlist>
<listitem>
<para>
retrieval via <literal>Get()</literal> or <literal>Load()</literal>
</para>
</listitem>
<listitem>
<para>
retrieval that happens implicitly when an association is navigated
</para>
</listitem>
<listitem>
<para>
<literal>ICriteria</literal> queries
</para>
</listitem>
<listitem>
<para>
HQL queries if <literal>subselect</literal> fetching is used
</para>
</listitem>
</itemizedlist>
<para>
No matter what fetching strategy you use, the defined non-lazy graph is guaranteed
to be loaded into memory. Note that this might result in several immediate selects
being used to execute a particular HQL query.
</para>
<para>
Usually, we don't use the mapping document to customize fetching. Instead, we
keep the default behavior, and override it for a particular transaction, using
<literal>left join fetch</literal> in HQL. This tells NHibernate to fetch
the association eagerly in the first select, using an outer join. In the
<literal>ICriteria</literal> query API, you would use
<literal>Fetch()</literal>.
</para>
<para>
If you ever feel like you wish you could change the fetching strategy used by
<literal>Get()</literal> or <literal>Load()</literal>, simply use a
<literal>ICriteria</literal> query, for example:
</para>
<programlisting><![CDATA[User user = session.CreateCriteria(typeof(User))
.Fetch(SelectMode.Fetch, "Permissions")
.Add( Expression.Eq("Id", userId) )
.UniqueResult<User>();]]></programlisting>
<para>
(This is NHibernate's equivalent of what some <emphasis>ORM</emphasis> solutions call a "fetch plan".)
</para>
<para>
A completely different way to avoid problems with N+1 selects is to use the
<link linkend="performance-cache">second-level cache</link>, or to enable
<link linkend="performance-fetching-batch">batch fetching</link>.
</para>
</sect2>
<sect2 id="performance-fetching-proxies" revision="2">
<title>Single-ended association proxies</title>
<para>
Lazy fetching for collections is implemented using NHibernate's own implementation
of persistent collections. However, a different mechanism is needed for lazy
behavior in single-ended associations. The target entity of the association must
be proxied. NHibernate implements lazy initializing proxies for persistent objects
using runtime bytecode enhancement.
</para>
<para>
By default, NHibernate generates proxies (at startup) for all persistent classes
and uses them to enable lazy fetching of <literal>many-to-one</literal> and
<literal>one-to-one</literal> associations.
</para>
<para>
The mapping file may declare an interface to use as the proxy interface for that
class, with the <literal>proxy</literal> attribute. By default, NHibernate uses a subclass
of the class. <emphasis>Note that the proxied class must implement a non-private default
constructor. We recommend this constructor for all persistent classes!</emphasis>
</para>
<para>
There are some gotchas to be aware of when extending this approach to polymorphic
classes, eg.
</para>
<programlisting><![CDATA[<class name="Cat" proxy="Cat">
......
<subclass name="DomesticCat">
.....
</subclass>
</class>]]></programlisting>
<para>
Firstly, instances of <literal>Cat</literal> will never be castable to
<literal>DomesticCat</literal>, even if the underlying instance is an
instance of <literal>DomesticCat</literal>:
</para>
<programlisting><![CDATA[// instantiate a proxy (does not hit the db)
Cat cat = session.Load<Cat>(id);
// hit the db to initialize the proxy
if ( cat.IsDomesticCat ) {
DomesticCat dc = (DomesticCat) cat; // Error!
....
}]]></programlisting>
<para>
Secondly, it is possible to break proxy <literal>==</literal>.
</para>
<programlisting><![CDATA[// instantiate a Cat proxy
Cat cat = session.Load<Cat>(id);
DomesticCat dc =
// acquire new DomesticCat proxy!
session.Load<DomesticCat>(id);
Console.WriteLine(cat == dc); // false]]></programlisting>
<para>
However, the situation is not quite as bad as it looks. Even though we now have two references
to different proxy objects, the underlying instance will still be the same object:
</para>
<programlisting><![CDATA[cat.Weight = 11.0; // hit the db to initialize the proxy
Console.WriteLine( dc.Weight ); // 11.0]]></programlisting>
<para>
Third, you may not use a proxy for a <literal>sealed</literal> class or a class
with any non-overridable public members.
</para>
<para>
Finally, if your persistent object acquires any resources upon instantiation (eg. in
initializers or default constructor), then those resources will also be acquired by
the proxy. The proxy class is an actual subclass of the persistent class.
</para>
<para>
These problems are all due to fundamental limitations in .NET's single inheritance model.
If you wish to avoid these problems your persistent classes must each implement an interface
that declares its business methods. You should specify these interfaces in the mapping file. eg.
</para>
<programlisting><![CDATA[<class name="CatImpl" proxy="ICat">
......
<subclass name="DomesticCatImpl" proxy="IDomesticCat">
.....
</subclass>
</class>]]></programlisting>
<para>
where <literal>CatImpl</literal> implements the interface <literal>ICat</literal> and
<literal>DomesticCatImpl</literal> implements the interface <literal>IDomesticCat</literal>. Then
proxies for instances of <literal>ICat</literal> and <literal>IDomesticCat</literal> may be returned
by <literal>Load()</literal> or <literal>Enumerable()</literal>. (Note that <literal>List()</literal>
does not usually return proxies.)
</para>
<programlisting><![CDATA[ICat cat = session.Load<CatImpl>(catid);
using(var iter = session
.CreateQuery("from CatImpl as cat where cat.Name='fritz'")
.Enumerable<CatImpl>()
.GetEnumerator())
{
iter.MoveNext();
ICat fritz = iter.Current;
}]]></programlisting>
<para>
Relationships are also lazily initialized. This means you must declare any properties to be of
type <literal>ICat</literal>, not <literal>CatImpl</literal>.
</para>
<para>
Certain operations do <emphasis>not</emphasis> require proxy initialization
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>Equals()</literal>, if the persistent class does not override
<literal>Equals()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>GetHashCode()</literal>, if the persistent class does not override
<literal>GetHashCode()</literal>
</para>
</listitem>
<listitem>
<para>
The identifier getter method
</para>
</listitem>
</itemizedlist>
<para>
NHibernate will detect persistent classes that override <literal>Equals()</literal> or
<literal>GetHashCode()</literal>.
</para>
<!--
<para>
By choosing <literal>lazy="no-proxy"</literal> instead of the default
<literal>lazy="proxy"</literal>, we can avoid the problems associated with typecasting.
However, we will require build-time bytecode instrumentation, and all operations
will result in immediate proxy initialization.
</para>
-->
</sect2>
<sect2 id="performance-fetching-initialization" revision="1">
<title>Initializing collections and proxies</title>
<para>
A <literal>LazyInitializationException</literal> will be thrown by NHibernate if an uninitialized
collection or proxy is accessed outside of the scope of the <literal>ISession</literal>, ie. when
the entity owning the collection or having the reference to the proxy is in the detached state.
</para>
<para>
Sometimes we need to ensure that a proxy or collection is initialized before closing the
<literal>ISession</literal>. Of course, we can alway force initialization by calling
<literal>cat.Sex</literal> or <literal>cat.Kittens.Count</literal>, for example.
But that is confusing to readers of the code and is not convenient for generic code.
</para>
<para>
The static methods <literal>NHibernateUtil.Initialize()</literal> and <literal>NHibernateUtil.IsInitialized()</literal>
provide the application with a convenient way of working with lazily initialized collections or
proxies. <literal>NHibernateUtil.Initialize(cat)</literal> will force the initialization of a proxy,
<literal>cat</literal>, as long as its <literal>ISession</literal> is still open.
<literal>NHibernateUtil.Initialize( cat.Kittens )</literal> has a similar effect for the collection
of kittens.
</para>
<para>
Another option is to keep the <literal>ISession</literal> open until all needed
collections and proxies have been loaded. In some application architectures,
particularly where the code that accesses data using NHibernate, and the code that
uses it are in different application layers or different physical processes, it
can be a problem to ensure that the <literal>ISession</literal> is open when a
collection is initialized. There are two basic ways to deal with this issue:
</para>
<itemizedlist>
<listitem>
<para>
In a web-based application, a <literal>HttpModule</literal> can be used
to close the <literal>ISession</literal> only at the very end of a user
request, once the rendering of the view is complete (the <emphasis>Open
Session in View</emphasis> pattern). Of course, this places heavy demands
on the correctness of the exception handling of your application infrastructure.
It is vitally important that the <literal>ISession</literal> is closed and the
transaction ended before returning to the user, even when an exception occurs
during rendering of the view. See the NHibernate Wiki for examples of this
"Open Session in View" pattern.
</para>
</listitem>
<listitem>
<para>
In an application with a separate business tier, the business logic must
"prepare" all collections that will be needed by the web tier before
returning. This means that the business tier should load all the data and
return all the data already initialized to the presentation/web tier that
is required for a particular use case. Usually, the application calls
<literal>NHibernateUtil.Initialize()</literal> for each collection that will
be needed in the web tier (this call must occur before the session is closed)
or retrieves the collection eagerly using a NHibernate query with a
<literal>FETCH</literal> clause or a <literal>SelectMode.Fetch</literal> in
<literal>ICriteria</literal>. This is usually easier if you adopt the
<emphasis>Command</emphasis> pattern instead of a <emphasis>Session Facade</emphasis>.
</para>
</listitem>
<listitem>
<para>
You may also attach a previously loaded object to a new <literal>ISession</literal>
with <literal>Merge()</literal> or <literal>Lock()</literal> before
accessing uninitialized collections (or other proxies). No, NHibernate does not,
and certainly <emphasis>should</emphasis> not do this automatically, since it
would introduce ad hoc transaction semantics!
</para>
</listitem>
</itemizedlist>
<para>
Sometimes you don't want to initialize a large collection, but still need some
information about it (like its size) or a subset of the data.
</para>
<para>
You can use a collection filter to get the size of a collection without initializing it:
</para>
<programlisting><![CDATA[s.CreateFilter(collection, "select count(*)").UniqueResult<long>()]]></programlisting>
<para>
The <literal>CreateFilter()</literal> method is also used to efficiently retrieve subsets
of a collection without needing to initialize the whole collection:
</para>
<programlisting><![CDATA[s.CreateFilter(lazyCollection, "").SetFirstResult(0).SetMaxResults(10).List<Entity>();]]></programlisting>
</sect2>
<sect2 id="performance-fetching-batch">
<title>Using batch fetching</title>
<para>
NHibernate can make efficient use of batch fetching, that is, NHibernate can load several uninitialized
proxies if one proxy is accessed (or collections). Batch fetching is an optimization of the lazy select
fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.
</para>
<para>
Batch fetching for classes/entities is easier to understand. Imagine you have the following situation
at runtime: You have 25 <literal>Cat</literal> instances loaded in an <literal>ISession</literal>, each
<literal>Cat</literal> has a reference to its <literal>Owner</literal>, a <literal>Person</literal>.
The <literal>Person</literal> class is mapped with a proxy, <literal>lazy="true"</literal>. If you now
iterate through all cats and call <literal>cat.Owner</literal> on each, NHibernate will by default
execute 25 <literal>SELECT</literal> statements, to retrieve the proxied owners. You can tune this
behavior by specifying a <literal>batch-size</literal> in the mapping of <literal>Person</literal>:
</para>
<programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
<para>
NHibernate will now execute only three queries, the pattern is 10, 10, 5.
</para>
<para>
You may also enable batch fetching of collections. For example, if each <literal>Person</literal> has
a lazy collection of <literal>Cat</literal>s, and 10 persons are currently loaded in the
<literal>ISession</literal>, iterating through all persons will generate 10 <literal>SELECT</literal>s,
one for every call to <literal>person.Cats</literal>. If you enable batch fetching for the
<literal>Cats</literal> collection in the mapping of <literal>Person</literal>, NHibernate can pre-fetch
collections:
</para>
<programlisting><![CDATA[<class name="Person">
<set name="Cats" batch-size="3">
...
</set>
</class>]]></programlisting>
<para>
With a <literal>batch-size</literal> of 3, NHibernate will load 3, 3, 3, 1 collections in four
<literal>SELECT</literal>s. Again, the value of the attribute depends on the expected number of
uninitialized collections in a particular <literal>Session</literal>.
</para>
<para>
Batch fetching of collections is particularly useful if you have a nested tree of items, ie.
the typical bill-of-materials pattern. (Although a <emphasis>nested set</emphasis> or a
<emphasis>materialized path</emphasis> might be a better option for read-mostly trees.)
</para>
<para>
<emphasis>Note:</emphasis> if you set <literal>default_batch_fetch_size</literal>
in configuration, NHibernate will configure the batch fetch optimization for lazy fetching
globally. Batch sizes specified at more granular level take precedence.
</para>
</sect2>
<sect2 id="performance-fetching-subselect">
<title>Using subselect fetching</title>
<para>
If one lazy collection or single-valued proxy has to be fetched, NHibernate loads all of
them, re-running the original query in a subselect. This works in the same way as
batch-fetching, without the piecemeal loading.
</para>
<!-- TODO: Write more about this -->
</sect2>
<!--
<sect2 id="performance-fetching-lazyproperties">
<title>Using lazy property fetching</title>
<para>
Hibernate3 supports the lazy fetching of individual properties. This optimization technique
is also known as <emphasis>fetch groups</emphasis>. Please note that this is mostly a
marketing feature, as in practice, optimizing row reads is much more important than
optimization of column reads. However, only loading some properties of a class might
be useful in extreme cases, when legacy tables have hundreds of columns and the data model
can not be improved.
</para>
<para>
To enable lazy property loading, set the <literal>lazy</literal> attribute on your
particular property mappings:
</para>
<programlisting><![CDATA[<class name="Document">
<id name="id">
<generator class="native"/>
</id>
<property name="name" not-null="true" length="50"/>
<property name="summary" not-null="true" length="200" lazy="true"/>
<property name="text" not-null="true" length="2000" lazy="true"/>
</class>]]></programlisting>
<para>
Lazy property loading requires build-time bytecode instrumentation! If your persistent
classes are not enhanced, NHibernate will silently ignore lazy property settings and
fall back to immediate fetching.
</para>
<para>
For bytecode instrumentation, use the following Ant task:
</para>
<programlisting><![CDATA[<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
<classpath path="${jar.path}"/>
<classpath path="${classes.dir}"/>
<classpath refid="lib.class.path"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${testclasses.dir}/org/hibernate/auction/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>]]></programlisting>
<para>
A different (better?) way to avoid unnecessary column reads, at least for
read-only transactions is to use the projection features of HQL or Criteria
queries. This avoids the need for build-time bytecode processing and is
certainly a preferred solution.
</para>
<para>
You may force the usual eager fetching of properties using <literal>fetch all
properties</literal> in HQL.
</para>
</sect2>
-->
</sect1>
<sect1 id="performance-cache" revision="1">
<title>The Second Level Cache</title>
<para>
A NHibernate <literal>ISession</literal> is a transaction-level cache of persistent data. It is
possible to configure a cluster or process-level (<literal>ISessionFactory</literal>-level) cache on
a class-by-class and collection-by-collection basis. You may even plug in a clustered cache. Be
careful. Caches are never aware of changes made to the persistent store by another application
(though they may be configured to regularly expire cached data).
<emphasis>In NHibernate 1.x the second level cache does not work correctly in combination with
distributed transactions.</emphasis>
</para>
<para>
The second level cache requires the use of transactions, be it through transaction scopes or NHibernate
transactions. Interacting with the data store without an explicit transaction is discouraged, and will
not allow the second level cache to work as intended.
</para>
<para>
By default, NHibernate uses HashtableCache for process-level caching. You may choose a different
implementation by specifying the name of a class that implements <literal>NHibernate.Cache.ICacheProvider</literal>
using the property <literal>cache.provider_class</literal>.
</para>
<table frame="topbot" id="cacheproviders" revision="1">
<title>Cache Providers</title>
<tgroup cols='5' align='left' colsep='1' rowsep='1'>
<colspec colname='c1' colwidth="1*"/>
<colspec colname='c2' colwidth="3*"/>
<colspec colname='c3' colwidth="1*"/>
<colspec colname='c4' colwidth="1*"/>
<colspec colname='c5' colwidth="1*"/>
<thead>
<row>
<entry>Cache</entry>
<entry>Provider class</entry>
<entry>Type</entry>
<entry>Cluster Safe</entry>
<entry>Query Cache Supported</entry>
</row>
</thead>
<tbody>
<row>
<entry>Hashtable (not intended for production use)</entry>
<entry><literal>NHibernate.Cache.HashtableCacheProvider</literal></entry>
<entry>memory</entry>
<entry></entry>
<entry>yes</entry>
</row>
<row>
<entry>ASP.NET Cache (System.Web.Cache)</entry>
<entry><literal>NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache</literal></entry>
<entry>memory</entry>
<entry></entry>
<entry>yes</entry>
</row>
<row>
<entry>Prevalence Cache</entry>
<entry><literal>NHibernate.Caches.Prevalence.PrevalenceCacheProvider, NHibernate.Caches.Prevalence</literal></entry>
<entry>memory, disk</entry>
<entry></entry>
<entry>yes</entry>
</row>
</tbody>
</tgroup>
</table>
<sect2 id="performance-cache-mapping">
<title>Cache mappings</title>
<para>
The <literal><cache></literal> element of a class or collection mapping has the
following form:
</para>
<programlistingco>
<areaspec>
<area id="cache1" coords="2 70"/>
<area id="cache2" coords="3 70"/>
</areaspec>
<programlisting><![CDATA[<cache
usage="read-write|nonstrict-read-write|read-only|never"
region="RegionName"
/>]]></programlisting>
<calloutlist>
<callout arearefs="cache1">
<para>
<literal>usage</literal> specifies the caching strategy:
<literal>read-write</literal>,
<literal>nonstrict-read-write</literal>,
<literal>read-only</literal> or
<literal>never</literal>
</para>
</callout>
<callout arearefs="cache2">
<para>
<literal>region</literal> (optional, defaults to the class or
collection role name) specifies the name of the second level cache
region
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Alternatively (preferably?), you may specify <literal><class-cache></literal> and
<literal><collection-cache></literal> elements in <literal>hibernate.cfg.xml</literal>.
</para>
<para>
The <literal>usage</literal> attribute specifies a <emphasis>cache concurrency strategy</emphasis>.
</para>
</sect2>
<sect2 id="performance-cache-readonly">
<title>Strategy: read only</title>
<para>
If your application needs to read but never modify instances of a persistent class, a
<literal>read-only</literal> cache may be used. This is the simplest and best performing
strategy. Its even perfectly safe for use in a cluster.
</para>
<programlisting><![CDATA[<class name="Eg.Immutable" mutable="false">
<cache usage="read-only"/>
....
</class>]]></programlisting>
</sect2>
<sect2 id="performance-cache-readwrite">
<title>Strategy: read/write</title>
<para>
If the application needs to update data, a <literal>read-write</literal> cache might be appropriate.
This cache strategy should never be used if serializable transaction isolation level is required.
You should ensure that the transaction is completed when <literal>ISession.Close()</literal> or
<literal>ISession.Disconnect()</literal> is called. If you wish to use this strategy in a cluster,
you should ensure that the underlying cache implementation supports locking. The built-in cache
providers do <emphasis>not</emphasis>.
</para>
<programlisting><![CDATA[<class name="eg.Cat" .... >
<cache usage="read-write"/>
....
<set name="Kittens" ... >
<cache usage="read-write"/>
....
</set>
</class>]]></programlisting>
</sect2>
<sect2 id="performance-cache-nonstrict">
<title>Strategy: nonstrict read/write</title>
<para>
If the application only occasionally needs to update data (ie. if it is extremely unlikely that two
transactions would try to update the same item simultaneously) and strict transaction isolation is
not required, a <literal>nonstrict-read-write</literal> cache might be appropriate.
When using this strategy you should ensure that the transaction is completed when
<literal>ISession.Close()</literal> or <literal>ISession.Disconnect()</literal> is called.
<!--
If the cache is used in a JTA environment, you must specify
<literal>hibernate.transaction.manager_lookup_class</literal>.
-->
</para>
</sect2>
<sect2 id="performance-cache-never">
<title>Strategy: never</title>
<para>
By default, without a cache configuration, entities are not cacheable. But they may still be referenced
in cacheable queries, which results will then be cached according to the values these non cacheable
entities have. So, their data may be indirectly cached through cacheable queries.
</para>
<para>
By using the cache strategy <literal>never</literal>, such indirect caching of these entities data will
be forbidden by NHibernate. Setting as cacheable a query referencing entities with strategy
<literal>never</literal> will be treated as an error by default. Alternatively, the
<literal>query.throw_never_cached</literal> <link linkend="configuration-optional">setting</link> can be
set to <literal>false</literal>: instead of raising an error, it will disable the query cache on such
queries, and log a warning.
</para>
</sect2>
<para>
The following table shows which providers are compatible with which concurrency strategies.
</para>
<table frame="topbot">
<title>Cache Concurrency Strategy Support</title>
<tgroup cols='4' align='left' colsep='1' rowsep='1'>
<colspec colname='c1' colwidth="1*"/>
<colspec colname='c2' colwidth="1*"/>
<colspec colname='c3' colwidth="1*"/>
<colspec colname='c4' colwidth="1*"/>
<thead>
<row>
<entry>Cache</entry>
<entry>read-only</entry>
<entry>nonstrict-read-write</entry>
<entry>read-write</entry>
</row>
</thead>
<tbody>
<row>
<entry>Hashtable (not intended for production use)</entry>
<entry>yes</entry>
<entry>yes</entry>
<entry>yes</entry>
</row>
<row>
<entry>SysCache</entry>
<entry>yes</entry>
<entry>yes</entry>
<entry>yes</entry>
</row>
<row>
<entry>PrevalenceCache</entry>
<entry>yes</entry>
<entry>yes</entry>
<entry>yes</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Refer to <xref linkend="caches" /> for more details.
</para>
</sect1>
<sect1 id="performance-sessioncache" revision="2">
<title>Managing the caches</title>
<para>
Whenever you pass an object to <literal>Save()</literal>, <literal>Update()</literal>
or <literal>SaveOrUpdate()</literal> and whenever you retrieve an object using
<literal>Load()</literal>, <literal>Get()</literal>, <literal>List()</literal>,
or <literal>Enumerable()</literal>, that object is added to the internal cache of the
<literal>ISession</literal>.
</para>
<para>
When <literal>Flush()</literal> is subsequently called, the state of that object will
be synchronized with the database. If you do not want this synchronization to occur or
if you are processing a huge number of objects and need to manage memory efficiently,
the <literal>Evict()</literal> method may be used to remove the object and its collections
from the first-level cache.
</para>
<programlisting><![CDATA[IEnumerable<Cat> cats = sess
.CreateQuery("from Eg.Cat as cat")
.List<Cat>(); //a huge result set
foreach (Cat cat in cats)
{
DoSomethingWithACat(cat);
sess.Evict(cat);
}]]></programlisting>
<para>
NHibernate will evict associated entities automatically if the association is mapped
with <literal>cascade="all"</literal> or <literal>cascade="all-delete-orphan"</literal>.
</para>
<para>
The <literal>ISession</literal> also provides a <literal>Contains()</literal> method
to determine if an instance belongs to the session cache.
</para>
<para>
To completely evict all objects from the session cache, call <literal>ISession.Clear()</literal>
</para>
<para>
For the second-level cache, there are methods defined on <literal>ISessionFactory</literal> for
evicting the cached state of an instance, entire class, collection instance or entire collection
role.
</para>
<programlisting><![CDATA[//evict a particular Cat
sessionFactory.Evict(typeof(Cat), catId);
//evict all Cats
sessionFactory.Evict(typeof(Cat));
//evict a particular collection of kittens
sessionFactory.EvictCollection("Eg.Cat.Kittens", catId);
//evict all kitten collections
sessionFactory.EvictCollection("Eg.Cat.Kittens");]]></programlisting>
</sect1>
<sect1 id="performance-querycache" revision="1">
<title>The Query Cache</title>
<para>
Query result sets may also be cached. This is only useful for queries that are run
frequently with the same parameters. To use the query cache you must first enable it:
</para>
<programlisting><![CDATA[<property name="cache.use_query_cache">true</property>>]]></programlisting>
<para>
This setting causes the creation of two new cache regions - one holding cached query
result sets (<literal>NHibernate.Cache.StandardQueryCache</literal>), the other
holding timestamps of the most recent updates to queryable tables
(<literal>UpdateTimestampsCache</literal>). Those region names will be prefixed by the
cache region prefix if <literal>cache.region_prefix</literal> setting is configured.
</para>
<para>
If you use a cache provider handling an expiration for cached entries, you should set
the <literal>UpdateTimestampsCache</literal> region expiration to a value greater than
the expiration of query cache regions. (Or disable its expiration.) Otherwise the query
cache may yield stale data.
</para>
<para>
Note that the query cache does not cache the state of any entities in the result set;
it caches only identifier values and results of value type. So the query cache should
always be used in conjunction with the second-level cache.
</para>
<para>
Most queries do not benefit from caching, so by default queries are not cached. To
enable caching, call <literal>IQuery.SetCacheable(true)</literal>. This call allows
the query to look for existing cache results or add its results to the cache when
it is executed.
</para>
<para>
If you require fine-grained control over query cache expiration policies, you may
specify a named cache region for a particular query by calling
<literal>IQuery.SetCacheRegion()</literal>.
</para>
<programlisting><![CDATA[var blogs = sess.CreateQuery("from Blog blog where blog.Blogger = :blogger")
.SetEntity("blogger", blogger)
.SetMaxResults(15)
.SetCacheable(true)
.SetCacheRegion("frontpages")