-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathfeatures.html
More file actions
1670 lines (1385 loc) · 116 KB
/
features.html
File metadata and controls
1670 lines (1385 loc) · 116 KB
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
<!DOCTYPE html>
<html lang="en">
<head>
<title>Features | Query | Ebean</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="2;URL='/docs/query'" />
<script async defer src="https://buttons.github.io/buttons.js"></script>
<link rel="shortcut icon" href="/images/favicon.ico" >
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
<link rel="stylesheet" href="/css/pygments.css" type="text/css" />
<link rel="stylesheet" href="/css/site.css">
</head>
<body>
<div id="wrap">
<div id="header">
<div class="navbar" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".mobile-nav">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<img src="/images/lg5-21x100.png" height="35">
</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav pull-right nav-btn-rhs">
<li ><a href="/"><span class="fas fa-home"></span> Home</a></li>
<li class="active"><a href="/docs">Docs</a></li>
<li ><a href="/docs/getting-started">Get Started</a></li>
<li ><a href="/support">Help</a></li>
<li ><a href="/videos">Videos</a></li>
<li ><a target="_blank" href="/apidoc/13">ApiDocs</a></li>
<li ><a href="/releases">Releases</a></li>
<li><a target="_blank" href="https://github.com/ebean-orm/ebean"> <i class="fab fa-github"></i></a></li>
</ul>
</div>
<div class="mobile-nav">
<ul class="nav navbar-nav pull-right nav-btn-rhs">
<li ><a href="/"><span class="fas fa-home"></span> Home</a></li>
<li class="active"><a href="/docs">Docs</a></li>
<li ><a href="/docs/getting-started">Get Started</a></li>
<li ><a href="/support">Help</a></li>
<li ><a href="/videos">Videos</a></li>
<li ><a target="_blank" href="/apidoc/13">ApiDocs</a></li>
<li ><a href="/releases">Releases</a></li>
<li><a target="_blank" href="https://github.com/ebean-orm/ebean"> <i class="fab fa-github"></i></a></li>
</ul>
</div>
</div>
</div>
</div>
<div id="main">
<div class="jumbotron mini">
<div class="container">
<h1><a href="/docs/">Documentation</a><span class="sep"> / </span><a href="/docs/query/">Query</a><span class="sep"> / </span><span class="last">Features</span></h1>
</div>
</div>
<div class="container doc-index bs-docs-container">
<div class="row">
<div class="col-md-9" role="main">
<form action="https://www.google.com/search" method="get">
<div class="page-inline-search" style="margin-bottom:8px;">
<div class="input-group">
<input name="q" id="searchinput" type="text" class="form-control" placeholder="Search... (press 's' to focus)" data-placeholder-focus="Search... (use '↑', '↓' and '⏎' to select results)" data-placeholder-blur="Search... (press 's' to focus)" autocomplete="off">
<input type="hidden" name="as_sitesearch" value="ebean.io">
<div class="input-group-btn">
<button class="btn btn-default form-control" type="submit"><i class="glyphicon glyphicon-search"></i></button>
</div>
</div>
<div class="search-results-inline-container" style="display: none;">
<ul id="search-results-container" class="search-results"><li class=" active"><a href="/docs" title="Docs"><span style="color:#777;">Docs</span> Documentation </a></li><li class=""><small style="color:#999;">And 101 more...</small></li></ul>
</div>
</div>
</form>
<div class="alert alert-danger">Redirecting as content is being updated. Please hold on tight ...</div>
<div class="bs-docs-section">
<h1 id="queries">Query Features</h1>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// find by id</span>
<span class="n">Order</span> <span class="n">order</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="mi">12</span><span class="o">);</span>
</pre></div>
</div>
executes the sql:
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="k">select</span> <span class="n">o</span><span class="p">.</span><span class="n">id</span><span class="p">,</span> <span class="n">o</span><span class="p">.</span><span class="n">order_date</span><span class="p">,</span> <span class="n">o</span><span class="p">.</span><span class="n">ship_date</span><span class="p">,</span> <span class="n">o</span><span class="p">.</span><span class="n">cretime</span><span class="p">,</span> <span class="n">o</span><span class="p">.</span><span class="n">updtime</span><span class="p">,</span> <span class="n">o</span><span class="p">.</span><span class="n">status_code</span><span class="p">,</span> <span class="n">o</span><span class="p">.</span><span class="n">customer_id</span>
<span class="k">from</span> <span class="n">or_order</span> <span class="n">o</span>
<span class="k">where</span> <span class="k">or</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="o">?</span>
</pre></div>
</div>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// these are the same</span>
<span class="n">Query</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">query</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">createQuery</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">Query</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">query</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</pre></div>
</div>
These two methods do exactly the same thing. The reason both
exist is because the createQuery() style is consistent with JPA and could be argued is a
better more accurate name. And find() is more consistent with the fluid API style.
<div class="alert alert-info">
<strong>Note:</strong> In Ebean 16.x, <code>DB.createQuery(Class, eqlString)</code> was supported for EQL queries.
This has been removed in Ebean 17.x. See <a href="/docs/query/eql">EQL documentation</a> and
<a href="/docs/upgrading">upgrading guide</a> for alternatives.
</div>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// fluid API style with find()</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span>
<span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">eq</span><span class="o">(</span><span class="s">"status.code"</span><span class="o">,</span> <span class="s">"SHIPPED"</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
<h2 id="where_clause">Where clause</h2>
<p>
You can specify 'predicates' for the where clause.
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// find all the orders shipped since a week ago</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">()</span>
<span class="o">.</span><span class="na">eq</span><span class="o">(</span><span class="s">"status"</span><span class="o">,</span> <span class="n">Order</span><span class="o">.</span><span class="na field">Status</span><span class="o">.</span><span class="na">SHIPPED</span><span class="o">)</span>
<span class="o">.</span><span class="na">gt</span><span class="o">(</span><span class="s">"shipDate"</span><span class="o">,</span> <span class="n">lastWeek</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
<h4>Example Predicates</h4>
<ul>
<li>eq(...) = equals</li>
<li>ne(...) = not equals
<li>
<li>ieq(...) = case insensitve equals</li>
<li>between(...) = between</li>
<li>gt(...) = greater than</li>
<li>ge(...) = greater than or equals</li>
<li>lt(...) = less than or equals</li>
<li>le(...) = less than or equals</li>
<li>isNull(...) = is null</li>
<li>isNotNull(...) = is not null</li>
<li>like(...) = like</li>
<li>startsWith(...) = string starts with</li>
<li>endswith(...) = string ends with</li>
<li>contains(...) = string conains</li>
<li>in(...) = in a subquery, collection or array</li>
<li>exists(...) = at least one row exists in a subquery</li>
<li>notExists(...) = no row exists in a subquery</li>
<li>more...</li>
</ul>
<p>
Use code completion in your favorite ide or see <a href="/apidoc/13/io.ebean.api/io/ebean/ExpressionList.html">ExpressionList</a>
class for more details.
</p>
<p>
Ebean will automatically add SQL joins if they are required for the where clause or order by clause (and the
matching joins are not explicitly included).
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">orders</span> <span class="o">=</span>
<span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">ilike</span><span class="o">(</span><span class="s">"customer.name"</span><span class="o">,</span> <span class="s">"rob%"</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
... in the sql below, the join to or_customer is automatically added to support the where clause.
<div class="syntax xml"><div class="highlight"><pre><span></span><span class="nt"><sql</span> <span class="na">summary=</span><span class="s">'[app.data.test.Order]'</span><span class="nt">></span>
select o.id, o.order_date, o.ship_date, o.cretime, o.updtime, o.status_code, o.customer_id
from or_order o
join or_customer c on o.customer_id = c.id
where lower(c.name) like ?
<span class="nt"></sql></span>
</pre></div>
</div>
<h2 id="query_language">Query language</h2>
<p>Ebean has it's own query language. Prior to this decision JPQL (the JPA query language)
was investigated to see if it would meet the desired goals of Ebean and it did not.
Specifically the desire for Ebean is to support "Partial Objects" via the query language and it is
difficult to
see how JPQL will evolve to support this (specifically difficulties around its select clause).
</p>
<p>
Apart from "Partial Object" support there was also a desire to simplify the join syntax,
specifically Ebean will automatically determine the type of join (outer join etc) for you and
also automatically add joins to support predicates and order by clauses.
</p>
<p>
JPQL is more powerful with the ability to mix entity beans with scalar values returning
Object[]. However, this feature also could be a major stumbling block for it to evolve
support for partial objects for any node in the object graph.
</p>
<p>
In summary you could say the Ebean query language is much simplier that JPQL with the
benefit of proper support for "Partial Objects" for any node in the object graph (this is not
possible with JPQL in it's current form).
</p>
<p>
"Partial Object" support in Ebean is important for design reasons and performance
reasons. From a performance perspective your queries are more performant if they fetch
less data back from the database. From a design perspective you do not need to model
using secondary tables but instead use partial objects at any depth in the query.
</p>
<p>
For example, to build an object graph for an Order you may want some product
information for each orderDetail.
</p>
<p>Examples
<div class="syntax groovy"><div class="highlight"><pre><span></span><span class="c1">// find all the orders fetching all the properties of order</span>
<span class="n">find</span> <span class="n">order</span>
<span class="c1">// find all the orders fetching all the properties of order</span>
<span class="c1">// ... this is the same as the first query</span>
<span class="n">find</span> <span class="nf">order</span> <span class="o">(*)</span>
<span class="c1">// find all the orders fetching the id, orderDate and shipDate</span>
<span class="c1">// ... This is described as a "partial object query"</span>
<span class="c1">// ... the ID property is *ALWAYS* fetched</span>
<span class="n">find</span> <span class="nf">order</span> <span class="o">(</span><span class="n">orderDate</span><span class="o">,</span> <span class="n">shipDate</span><span class="o">)</span>
<span class="c1">// find all the orders (and orderDetails)</span>
<span class="c1">// ... fetching all the properties of order</span>
<span class="c1">// ... and all the properties of orderDetails</span>
<span class="c1">// ... the type of fetch(Outer etc) is determined automatically</span>
<span class="n">find</span> <span class="n">order</span>
<span class="n">fetch</span> <span class="n">orderDetails</span>
<span class="c1">// find all the orders (with their orderDetails)</span>
<span class="c1">// ... fetching all the properties of order</span>
<span class="c1">// ... and all the properties of orderDetails</span>
<span class="n">find</span> <span class="nf">order</span> <span class="o">(*)</span>
<span class="n">fetch</span> <span class="nf">orderDetails</span> <span class="o">(*)</span>
<span class="c1">// find all the orders (with orderDetails and products)</span>
<span class="c1">// ... fetching the order id, orderDate and shipDate</span>
<span class="c1">// ... fetching all the properties for orderDetail</span>
<span class="c1">// ... fetching the product id, sku and name</span>
<span class="n">find</span> <span class="nf">order</span> <span class="o">(</span><span class="n">orderDate</span><span class="o">,</span> <span class="n">shipDate</span><span class="o">)</span>
<span class="n">fetch</span> <span class="nf">orderDetails</span> <span class="o">(*)</span>
<span class="n">fetch</span> <span class="n">orderDetails</span><span class="o">.</span><span class="na">product</span> <span class="o">(</span><span class="n">sku</span><span class="o">,</span> <span class="n">name</span><span class="o">)</span>
</pre></div>
</div>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">String</span> <span class="n">query</span> <span class="o">=</span> <span class="s">"find order where status.code=:status and shipDate > :shipped"</span><span class="o">;</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">setQuery</span><span class="o">(</span><span class="n">query</span><span class="o">)</span>
<span class="o">.</span><span class="na">setParameter</span><span class="o">(</span><span class="s">"status"</span><span class="o">,</span> <span class="n">Order</span><span class="o">.</span><span class="na field">Status</span><span class="o">.</span><span class="na">SHIPPED</span><span class="o">)</span>
<span class="o">.</span><span class="na">setParameter</span><span class="o">(</span><span class="s">"shipped"</span><span class="o">,</span> <span class="n">lastWeek</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
<div class="syntax xml"><div class="highlight"><pre><span></span><span class="nt"><sql</span> <span class="na">summary=</span><span class="s">'[app.data.test.Order] autoFetchTuned[false]'</span><span class="nt">></span>
select o.id, o.order_date, o.ship_date, o.cretime, o.updtime, o.status_code, o.customer_id
from or_order o
where o.status_code = ? and o.ship_date > ?
<span class="nt"></sql></span>
</pre></div>
</div>
<p>Every object in the object graph can be a partial object. This is what you can't do in JPQL
currently.</p>
<p>These Partially populated objects are will lazy load as required and are fully updatable etc.
You can treat them just like fully populated objects.</p>
<p>Autofetch can use partial objects to only fetch the properties that the application actually
uses. In this way you can get the performance of partial objects without any work on your
part (Autofetch determines the joins and properties to fetch for you).</p>
<h2 id="partial_objects">Partial objects</h2>
<p>
The Query object has select() and fetch() methods and these allow you to specify
the properties that should be fetched.
</p>
<p>This can be a very significant performance benefit by only fetching the properties you need. If
the properties are in DB indexes then the DB doesn't have to read Index Blocks. This is also a
design benefit in that it removes the "fixed" design constraints of using secondary table
properties.</p>
<p>Note that <a href="#autofetch">Autofetch</a> can use profiling to specify the properties to
select rather than you manually doing so (which saves you the work).</p>
<h4>Select</h4>
<p>
With select you specify the properties that should be fetched on the root level type.
</p>
<p>
Partially loading a bean or object graph using select() and fetch().
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">Customer</span> <span class="n">customer</span> <span class="o">=</span> <span class="n">Customer</span><span class="o">.</span><span class="na">find</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"name"</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">idEq</span><span class="o">(</span><span class="mi">1L</span><span class="o">)</span>
<span class="o">.</span><span class="na">findOne</span><span class="o">();</span>
</pre></div>
</div>
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="k">select</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="n">c0</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">name</span> <span class="n">c1</span>
<span class="k">from</span> <span class="n">customer</span> <span class="n">t0</span>
<span class="k">where</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="o">?</span> <span class="p">;</span> <span class="c1">--bind(1)</span>
</pre></div>
</div>
</p>
<h4>Fetch</h4>
<p>
With fetch you specify the properties that should be fetched on the associated beans.
</p>
<p>*ANY* node of the object graph can be a partial object. This example shows several ways partial
objects can be fetched.
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">Order</span> <span class="n">order</span> <span class="o">=</span> <span class="n">Order</span><span class="o">.</span><span class="na">find</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"status, orderDate, shipDate"</span><span class="o">)</span> <span class="c1">// 3 fields from order</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">,</span> <span class="s">"name"</span><span class="o">)</span> <span class="c1">// just name field from customer</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details"</span><span class="o">)</span> <span class="c1">// all fields from details</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details.product"</span><span class="o">,</span> <span class="s">"sku"</span><span class="o">)</span> <span class="c1">// just sku field from product</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">idEq</span><span class="o">(</span><span class="mi">1L</span><span class="o">)</span>
<span class="o">.</span><span class="na">findOne</span><span class="o">();</span>
</pre></div>
</div>
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="k">select</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="n">c0</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">status</span> <span class="n">c1</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">order_date</span> <span class="n">c2</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">ship_date</span> <span class="n">c3</span><span class="p">,</span>
<span class="n">t1</span><span class="p">.</span><span class="n">id</span> <span class="n">c4</span><span class="p">,</span> <span class="n">t1</span><span class="p">.</span><span class="n">name</span> <span class="n">c5</span><span class="p">,</span> <span class="c1">-- customer name</span>
<span class="n">t2</span><span class="p">.</span><span class="n">id</span> <span class="n">c6</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">order_qty</span> <span class="n">c7</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">ship_qty</span> <span class="n">c8</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">unit_price</span> <span class="n">c9</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="k">version</span> <span class="n">c10</span><span class="p">,</span>
<span class="n">t2</span><span class="p">.</span><span class="n">when_created</span> <span class="n">c11</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">when_updated</span> <span class="n">c12</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">order_id</span> <span class="n">c13</span><span class="p">,</span>
<span class="n">t3</span><span class="p">.</span><span class="n">id</span> <span class="n">c14</span><span class="p">,</span> <span class="n">t3</span><span class="p">.</span><span class="n">sku</span> <span class="n">c15</span> <span class="c1">-- product sku</span>
<span class="k">from</span> <span class="n">orders</span> <span class="n">t0</span>
<span class="k">join</span> <span class="n">customer</span> <span class="n">t1</span> <span class="k">on</span> <span class="n">t1</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">t0</span><span class="p">.</span><span class="n">customer_id</span>
<span class="k">left</span> <span class="k">outer</span> <span class="k">join</span> <span class="n">order_detail</span> <span class="n">t2</span> <span class="k">on</span> <span class="n">t2</span><span class="p">.</span><span class="n">order_id</span> <span class="o">=</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span>
<span class="k">left</span> <span class="k">outer</span> <span class="k">join</span> <span class="n">product</span> <span class="n">t3</span> <span class="k">on</span> <span class="n">t3</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">t2</span><span class="p">.</span><span class="n">product_id</span>
<span class="k">where</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="o">?</span>
<span class="k">order</span> <span class="k">by</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">id</span> <span class="k">asc</span><span class="p">;</span> <span class="c1">--bind(1)</span>
</pre></div>
</div>
</p>
<h4>Saving a partial object</h4>
<p>
You can save or delete a Partial Object.
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// find customer 1</span>
<span class="c1">// ... just fetch the customer id, name and version property</span>
<span class="n">Customer</span> <span class="n">customer</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Customer</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"name"</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">idEq</span><span class="o">(</span><span class="mi">1</span><span class="o">)</span>
<span class="o">.</span><span class="na">findOne</span><span class="o">();</span>
<span class="n">customer</span><span class="o">.</span><span class="na">setName</span><span class="o">(</span><span class="s">"CoolName"</span><span class="o">);</span>
<span class="n">DB</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">customer</span><span class="o">);</span>
</pre></div>
</div>
<p>
The query generates the following SQL...
<div class="syntax xml"><div class="highlight"><pre><span></span><span class="nt"><sql</span> <span class="na">summary=</span><span class="s">'[app.data.test.Customer]'</span><span class="nt">></span>
select c.id, c.name
from or_customer c
where c.id = ?
<span class="nt"></sql></span>
</pre></div>
</div>
</p>
<p>and in the transaction logs we can find the update dml and bind values.</p>
<div class="syntax xml"><div class="highlight"><pre><span></span>... update or_customer set name=?, updtime=? where id=? and name=?
... Binding Update [or_customer] set[name=CoolName, updtime=2008-11-19 10:58:08.598, ] where[id=1,
name=Ford, ]
... Updated [app.data.test.Customer] [1]
</pre></div>
</div>
<h2 id="query_joins">Query joins</h2>
<h4>
Controlling eager loading of the object graph using query joins.
</h4>
<p>You can use fetch() to explicitly state which additional paths you want to fetch. You would do
this to reduce "lazy loading" of those beans later.</p>
<p>
When more than 1 OneToMany or ManyToMany relationship is eagerly fetched
then Ebean will automatically convert one of those into a 'Query Join'. Ebean does this so that it
avoids
generating a cartesian product query. Note that Ebean determines the type of SQL join for you
based on the cardinality and optionality of the relationship.
</p>
<h4>Example 1</h4>
<p>
It this example both "customer.contacts" and "details" are OneToMany relationships.
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">orders</span> <span class="o">=</span> <span class="n">Order</span><span class="o">.</span><span class="na">find</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"status"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer.contacts"</span><span class="o">)</span> <span class="c1">// contacts is a @OneToMany</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details"</span><span class="o">)</span> <span class="c1">// details is a @OneToMany</span>
<span class="o">.</span><span class="na">orderBy</span><span class="o">(</span><span class="s">"customer.name"</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
... results in the "left outer join contact" and related t2.* columns being included in the
query (so they won't be lazy loaded later).
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="c1">-- This first query includes the customer contacts</span>
<span class="c1">-- but does not include the order details</span>
<span class="k">select</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="n">c0</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">status</span> <span class="n">c1</span><span class="p">,</span>
<span class="n">t1</span><span class="p">.</span><span class="n">id</span> <span class="n">c2</span><span class="p">,</span> <span class="n">t1</span><span class="p">.</span><span class="n">inactive</span> <span class="n">c3</span><span class="p">,</span> <span class="n">t1</span><span class="p">.</span><span class="n">name</span> <span class="n">c4</span><span class="p">,</span> <span class="p">...</span> <span class="c1">-- truncated</span>
<span class="n">t2</span><span class="p">.</span><span class="n">id</span> <span class="n">c12</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">first_name</span> <span class="n">c13</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">last_name</span> <span class="n">c14</span><span class="p">,</span> <span class="p">...</span> <span class="c1">-- truncated</span>
<span class="n">t2</span><span class="p">.</span><span class="n">when_updated</span> <span class="n">c19</span><span class="p">,</span> <span class="n">t2</span><span class="p">.</span><span class="n">customer_id</span> <span class="n">c20</span>
<span class="k">from</span> <span class="n">orders</span> <span class="n">t0</span>
<span class="k">join</span> <span class="n">customer</span> <span class="n">t1</span> <span class="k">on</span> <span class="n">t1</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">t0</span><span class="p">.</span><span class="n">customer_id</span>
<span class="k">left</span> <span class="k">outer</span> <span class="k">join</span> <span class="n">contact</span> <span class="n">t2</span> <span class="k">on</span> <span class="n">t2</span><span class="p">.</span><span class="n">customer_id</span> <span class="o">=</span> <span class="n">t1</span><span class="p">.</span><span class="n">id</span>
<span class="k">order</span> <span class="k">by</span> <span class="n">t1</span><span class="p">.</span><span class="n">name</span><span class="p">;</span>
</pre></div>
</div>
</p>
<h4>The 'Query Join' query ...</h4>
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="c1">-- This second query fetchs the order details associated</span>
<span class="c1">-- with the orders that were returned by the first query</span>
<span class="k">select</span> <span class="n">t0</span><span class="p">.</span><span class="n">order_id</span> <span class="n">c0</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="n">c1</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">order_qty</span> <span class="n">c2</span><span class="p">,</span> <span class="p">...</span>
<span class="k">from</span> <span class="n">order_detail</span> <span class="n">t0</span>
<span class="k">where</span> <span class="p">(</span><span class="n">t0</span><span class="p">.</span><span class="n">order_id</span><span class="p">)</span> <span class="k">in</span> <span class="p">(</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">)</span>
<span class="k">order</span> <span class="k">by</span> <span class="n">t0</span><span class="p">.</span><span class="n">order_id</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span><span class="p">;</span>
<span class="c1">--bind(2,1,1,1,3,3,3,4,4,4)</span>
</pre></div>
</div>
<h4>Example 2</h4>
<p>
In this example contacts is a @OneToMany and then off from each contact notes is a @OneToMany.
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">List</span><span class="o"><</span><span class="n">Customer</span><span class="o">></span> <span class="n">customers</span> <span class="o">=</span> <span class="n">Customer</span><span class="o">.</span><span class="na">find</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"contacts"</span><span class="o">)</span> <span class="c1">// contacts is a OneToMany</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"contacts.notes"</span><span class="o">)</span> <span class="c1">// notes is a OneToMany</span>
<span class="o">.</span><span class="na">orderBy</span><span class="o">(</span><span class="s">"name"</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="k">select</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="n">c0</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">inactive</span> <span class="n">c1</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">name</span> <span class="n">c2</span><span class="p">,</span> <span class="p">...</span>
<span class="n">t0</span><span class="p">.</span><span class="n">when_updated</span> <span class="n">c7</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">billing_address_id</span> <span class="n">c8</span><span class="p">,</span> <span class="p">...</span>
<span class="n">t1</span><span class="p">.</span><span class="n">id</span> <span class="n">c10</span><span class="p">,</span> <span class="n">t1</span><span class="p">.</span><span class="n">first_name</span> <span class="n">c11</span><span class="p">,</span> <span class="n">t1</span><span class="p">.</span><span class="n">last_name</span> <span class="n">c12</span><span class="p">,</span> <span class="p">...</span>
<span class="k">from</span> <span class="n">customer</span> <span class="n">t0</span>
<span class="k">left</span> <span class="k">outer</span> <span class="k">join</span> <span class="n">be_contact</span> <span class="n">t1</span> <span class="k">on</span> <span class="n">t1</span><span class="p">.</span><span class="n">customer_id</span> <span class="o">=</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span>
<span class="k">order</span> <span class="k">by</span> <span class="n">t0</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span>
</pre></div>
</div>
<h4>The 'Query Join' query ...</h4>
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="c1">-- fetch the contact notes for all the contacts</span>
<span class="k">select</span> <span class="n">t0</span><span class="p">.</span><span class="n">contact_id</span> <span class="n">c0</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">id</span> <span class="n">c1</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">contact_id</span> <span class="n">c2</span><span class="p">,</span> <span class="n">t0</span><span class="p">.</span><span class="n">title</span> <span class="n">c3</span><span class="p">,</span> <span class="p">...</span>
<span class="k">from</span> <span class="n">contact_note</span> <span class="n">t0</span>
<span class="k">where</span> <span class="p">(</span><span class="n">t0</span><span class="p">.</span><span class="n">contact_id</span><span class="p">)</span> <span class="k">in</span> <span class="p">(</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">,</span><span class="o">?</span><span class="p">)</span>
</pre></div>
</div>
<h2 id="fetchconfig">FetchConfig</h2>
<p>When you specify a Query with Ebean it can result in more than 1 SQL query. Sometimes
you want explicit control over this (what the secondary queries are, batch size used, eager
or lazily invoked)</p>
<p>FetchConfig gives you the ability to specify these "secondary queries" and let them
executed lazily ("lazy loading join") or eagerly ("query join").</p>
<p>Note that explicitly using FetchConfig is not a requirement. Ebean is able to automatically
convert some joins
to "query joins" when it is needed such as when building object graphs with multiple *ToMany
relationships or when limit offset
is used with a *ToMany relationship.</p>
<p>You use FetchConfig to define that you want to use a separate SQL query to fetch that part of
the object graph (to use a "query join" rather than a "fetch join"). What this means is that
Ebean will use 2 SQL queries rather than 1 to build the object graph.
</p>
<p>Note that you do not need to expicitly use FetchConfig if you don't want to. In that case Ebean
will automatically convert any fetch paths over to use FetchConfig if it needs to (multiple
*ToMany relationships etc).
</p>
<p>FetchConfig defines the configuration options for a "query fetch" or a "lazy loading fetch".
This gives you the ability to use multiple smaller queries to populate an object graph as
opposed to a single large query. The primary goal is to provide efficient ways of loading
complex object graphs avoiding SQL Cartesian product and issues around populating object graphs
that have multiple *ToMany relationships. It also provides the ability to control the lazy
loading queries (batch size, selected properties and fetches) to avoid N+1 queries etc.
</p>
<p>
There can also be cases loading across a single OneToMany where 2 SQL queries using Ebean
FetchConfig.query() can be more efficient than one SQL query. When the "One" side is wide (lots
of columns) and the cardinality difference is high (a lot of "Many" beans per "One" bean) then
this can be more efficient loaded as 2 SQL queries.
</p>
<p>The reason for using "Query Joins" as opposed to "Fetch joins" is that there are some
cases where using multiple queries is more efficient that a single query.
<p>
<p>Any time you want to load multiple OneToMany associations it will likely be more
performant as multiple SQL queries. If a single SQL query was used that would result in a
Cartesian product.</p>
<p>There can also be cases loading across a single OneToMany where 2 SQL queries (using
Ebean "query join") can be more efficient than one SQL query (using Ebean "fetch join").
When the "One" side is wide (lots of columns) and the cardinality difference is high (a lot
of "Many" beans per "One" bean) then this can be more efficient loaded as 2 SQL queries.</p>
<p>
Example: Find Orders join details using a single SQL query
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// Normal fetch join results in a single SQL query</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">Ebean</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">).</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details"</span><span class="o">).</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
<p>
<p>
Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL queries
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// This will use 2 SQL queries to build this object graph.</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">query</span><span class="o">())</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
queries:
<ol>
<li>find order</li>
<li>find orderDetails where order.id in (?,?...) // first 100 order id's</li>
</ol>
</p>
<p>
Example: Using 2 "query joins"
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// This will use 3 SQL queries to build this object graph</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span>
<span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">query</span><span class="o">())</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">queryFirst</span><span class="o">(</span><span class="mi">5</span><span class="o">))</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
queries:
<ol>
<li>find order</li>
<li>find orderDetails where order.id in (?,?...) // first 100 order id's</li>
<li>find customer where id in (?,?,?,?,?) // first 5 customers</li>
</ol>
</p>
<p>
Example: Using "query joins" and partial objects
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// This will use 3 SQL queries to build this object graph</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span>
<span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"status, shipDate"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details"</span><span class="o">,</span> <span class="s">"quantity, price"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">query</span><span class="o">())</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details.product"</span><span class="o">,</span> <span class="s">"sku, name"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">,</span> <span class="s">"name"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">queryFirst</span><span class="o">(</span><span class="mi">5</span><span class="o">))</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer.contacts"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer.shippingAddress"</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
queries:
<ol>
<li>find order (status, shipDate)</li>
<li>find orderDetail (quantity, price) fetch product (sku, name) where order.id in (?,?
...)
</li>
<li>find customer (name) fetch contacts (*) fetch shippingAddress (*) where id in
(?,?,?,?,?)
</li>
</ol>
Note: the fetch of "details.product" is automatically included into the fetch of "details"<br>
Note: the fetch of "customer.contacts" and "customer.shippingAddress" are automatically
included in the fetch of "customer"
</p>
<p>You can use query() and lazy together on a single join. The query is executed immediately
and the lazy defines the batch size to use for further lazy loading (if lazy loading is
invoked).
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span>
<span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">query</span><span class="o">(</span><span class="mi">10</span><span class="o">).</span><span class="na">lazy</span><span class="o">(</span><span class="mi">5</span><span class="o">))</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
queries:
<ol>
<li>find order</li>
<li>find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers</li>
<li>then if lazy loading of customers is invoked, use a batch size of 5 to load the
customers
</li>
</ol>
</p>
<p>
Example of controlling the lazy loading query:
</p>
<p>
This gives us the ability to optimise the lazy loading query for a given use case.
List<|Order> list = DB.find(Order.class)
.fetch("customer","name", new FetchConfig().lazy(5))
.fetch("customer.contacts","contactName, phone, email")
.fetch("customer.shippingAddress")
.where().eq("status",Order.Status.NEW)
.findList();
queries:
<ol>
<li>find order where status = Order.Status.NEW</li>
<li>if lazy loading of customers is invoked, use a batch size of 5 to load the
customers.
</p>
</ol>
Note: when the laxy loading of customers is performed, it perform like:
<div class="syntax xml"><div class="highlight"><pre><span></span>find customer (name)
fetch customer.contacts (contactName, phone, email)
fetch customer.shippingAddress (*)
where id in (?,?,?,?,?)
</pre></div>
</div>
<p>Example of wo "Query Joins" results in 3 SQL queries used to build an object graph
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// A more advanced example with multiple query joins</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">l0</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"status, shipDate"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details"</span><span class="o">,</span> <span class="s">"orderQty, unitPrice"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">query</span><span class="o">())</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"details.product"</span><span class="o">,</span> <span class="s">"sku, name"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">,</span> <span class="s">"name"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">query</span><span class="o">(</span><span class="mi">10</span><span class="o">))</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer.contacts"</span><span class="o">,</span><span class="s">"firstName, lastName, mobile"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer.shippingAddress"</span><span class="o">,</span><span class="s">"line1, city"</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
The resulting 3 sql queries are:
</p>
<p>
Query 1 - the main query - Note: customer_id was automatically added to support query join.
<div class="syntax xml"><div class="highlight"><pre><span></span>// query 1 … the main query
<span class="nt"><sql</span> <span class="na">summary=</span><span class="s">'Order'</span><span class="nt">></span>
select o.id c0, o.status c1, o.ship_date c2, o.customer_id c3
from orders o
<span class="nt"></sql></span>
</pre></div>
</div>
</p>
<p>
Query 2 - query join on customer - fetching the first 10 customers referenced (batch:10) but
there where actually only 2 to fetch (actual:2).
<div class="syntax xml"><div class="highlight"><pre><span></span><span class="nt"><sql</span> <span class="na">mode=</span><span class="s">'+query'</span> <span class="na">summary=</span><span class="s">'Customer, shippingAddress</span>
<span class="s">y:contacts'</span> <span class="na">load=</span><span class="s">'path:customer batch:10 actual:2'</span><span class="nt">></span>
select c.id c0, c.name c1
, cs.id c2, cs.line_1 c3, cs.city c4
, cc.id c5, cc.first_name c6, cc.last_name c7, cc.mobile c8
from customer c
left outer join address cs on cs.id = c.shipping_address_id
left outer join contact cc on cc.customer_id = c.id
where c.id in (?,?,?,?,?,?,?,?,?,?)
order by c.id
<span class="nt"></sql></span>
</pre></div>
</div>
</p>
<p>
Query 3 – query join on details - fetching the order details for the first 100 orders
(batch:100).
<div class="syntax xml"><div class="highlight"><pre><span></span><span class="nt"><sql</span> <span class="na">mode=</span><span class="s">'+query'</span> <span class="na">summary=</span><span class="s">'Order +many:details, details.product'</span>
<span class="na">load=</span><span class="s">'path:details batch:100 actual:3'</span><span class="nt">></span>
select o.id c0
, od.id c1, od.order_qty c2, od.unit_price c3
, odp.id c4, odp.sku c5, odp.name c6
from orders o
left outer join order_detail od on od.order_id = o.id
left outer join product odp on odp.id = od.product_id
where o.id in (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
order by o.id
<span class="nt"></sql></span>
</pre></div>
</div>
<h4>FetchConfig.lazy() - "Lazy Joins"</h4>
<p>If a join is not defined at all (neither a fetch join or a query join) – then lazy loading
will by
default just fetch all the properties for that entity.
</p>
<p>
FetchConfig.lazy() allows you to control that lazy loading query – define the batch size,
properties to select and also fetch paths to include on the lazy load query.
</p>
<p>
This is very similar to a "query join" except that the loading occurs on demand (when the
property is requested and not already loaded).
</p>
<p>
The reason you would want to control the lazy loading query is to optimise performance
for further lazy loading (avoid N+1 queries, define joins that should be included for lazy
loading queries, load only the properties required and no more).
</p>
<p>
Example: Control the query used to lazy load
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// control the lazy loading of customers ...</span>
<span class="n">List</span><span class="o"><</span><span class="n">Order</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer"</span><span class="o">,</span><span class="s">"name"</span><span class="o">,</span> <span class="k">new</span> <span class="n">FetchConfig</span><span class="o">().</span><span class="na">lazy</span><span class="o">(</span><span class="mi">5</span><span class="o">))</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer.contacts"</span><span class="o">,</span><span class="s">"contactName, phone, email"</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"customer.shippingAddress"</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">eq</span><span class="o">(</span><span class="s">"status"</span><span class="o">,</span><span class="n">Order</span><span class="o">.</span><span class="na field">Status</span><span class="o">.</span><span class="na">NEW</span><span class="o">)</span>
<span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
<p>
In the example above the orders are loaded. Only when the application requests a
customer property (that is not the customer's id) then the lazy loading of the customer is
invoked. At that point the customer name is loaded, with the contacts and
shippingAddress – this is done in batch of 5 customers.
</p>
<p>
Note that if the customer status is requested (rather than the customer name) and that
invokes the lazy loading then all the customer's properties are loaded (rather than just the
customers name).
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">Order</span> <span class="n">order</span> <span class="o">=</span> <span class="n">list</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="n">Customer</span> <span class="n">customer</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="na">getCustomer</span><span class="o">();</span>
<span class="c1">// this invokes the lazy loading of 5 customers</span>
<span class="n">String</span> <span class="n">name</span> <span class="o">=</span> <span class="n">customer</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span>
</pre></div>
</div>
</p>
<p>
The resulting lazy loading query is …
<div class="syntax xml"><div class="highlight"><pre><span></span><span class="nt"><sql</span> <span class="na">mode=</span><span class="s">'+lazy'</span> <span class="na">summary=</span><span class="s">'Customer, shippingAddress +many:contacts'</span>
<span class="na">load=</span><span class="s">'path:customer batch:5 actual:2'</span><span class="nt">></span>
select c.id c0, c.name c1, cs.id c2, cs.line_1 c3, cs.line_2 c4, cs.city c5,
cs.cretime c6, cs.updtime c7, cs.country_code c8,
cc.id c9, cc.phone c10, cc.email c11
from customer c
left outer join address cs on cs.id = c.shipping_address_id
left outer join contact cc on cc.customer_id = c.id
where c.id in (?,?,?,?,?)
order by c.id
<span class="nt"></sql></span>
</pre></div>
</div>
<h4>Using both</h4>
<p>You can use both queryFirst() and lazy() on a single join. The queryFirst() part defines
the
number of beans that will be loaded eagerly via an additional query and then lazy defines
the batch size of the lazy loading that occurs after than (if there is any).
<div class="syntax java"><div class="highlight"><pre><span></span><span class="k">new</span> <span class="n">FetchQuery</span><span class="o">.</span><span class="na">queryFirst</span><span class="o">(</span><span class="mi">100</span><span class="o">).</span><span class="na">lazy</span><span class="o">(</span><span class="mi">10</span><span class="o">);</span>
</pre></div>
</div>
</p>
<h4>+query and +lazy – query language syntax</h4>
<p>
To define "query joins" and "lazy joins" in the query language you can use +query and
+lazy. Optionally you can specify the batch size for both.
<div class="syntax groovy"><div class="highlight"><pre><span></span><span class="n">find</span> <span class="n">order</span>
<span class="n">join</span> <span class="nf">customers</span> <span class="o">(+</span><span class="n">query</span> <span class="o">)</span>
<span class="n">where</span> <span class="n">status</span> <span class="o">=</span> <span class="o">:</span><span class="n">status</span>
</pre></div>
</div>
<div class="syntax groovy"><div class="highlight"><pre><span></span><span class="n">find</span> <span class="nf">order</span> <span class="o">(</span><span class="n">status</span><span class="o">,</span> <span class="n">shipDate</span><span class="o">)</span>
<span class="n">join</span> <span class="nf">customers</span> <span class="o">(+</span><span class="n">lazy</span><span class="o">(</span><span class="mi">10</span><span class="o">)</span> <span class="n">name</span><span class="o">,</span> <span class="n">status</span><span class="o">)</span>
<span class="n">where</span> <span class="n">status</span> <span class="o">=</span> <span class="o">:</span><span class="n">orderStatus</span>
</pre></div>
</div>
</p>
<h2 id="lazy_loading">Lazy loading</h2>
<h4>
Fine grained control over lazy loading.
</h4>
<p>
A Partial Object will lazy load the rest of the data on demand when you get or set a
property it does not have.
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// find order 12</span>
<span class="c1">// ... fetching the order id, orderDate and version property</span>
<span class="c1">// .... nb: the Id and version property are always fetched</span>
<span class="n">Order</span> <span class="n">order</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Order</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"orderDate"</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">idEq</span><span class="o">(</span><span class="mi">12</span><span class="o">)</span>
<span class="o">.</span><span class="na">findOne</span><span class="o">();</span>
<span class="c1">// shipDate is not in the partially populated order</span>
<span class="c1">// ... so it will lazy load all the missing properties</span>
<span class="n">Date</span> <span class="n">shipDate</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="na">getShipDate</span><span class="o">();</span>
<span class="c1">// similarly if we where to set the shipDate</span>
<span class="c1">// ... that would also trigger a lazy load</span>
<span class="n">order</span><span class="o">.</span><span class="na">setShipDate</span><span class="o">(</span><span class="k">new</span> <span class="n">Date</span><span class="o">());</span>
</pre></div>
</div>
<p>
Lazy loading occurs automatically when you set or get a property that the partially
populated bean does not have.
</p>
<h2 id="named_queries">Named Queries</h2>
<div class="alert alert-warning">
<strong>⚠️ Ebean 16.x primarily:</strong> Named Queries using EQL syntax were a feature of Ebean 16.x.
The EQL syntax within <code>@NamedQuery</code> annotations is no longer actively developed in Ebean 17.x.
For new code, use <a href="/docs/query/query-beans">Query Beans</a> or <a href="/docs/query/where">standard Query API</a> directly.
See <a href="/docs/query/eql">EQL documentation</a> and <a href="/docs/upgrading">upgrading guide</a>.
</div>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">jakarata.persistence.NamedQueries</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarata.persistence.NamedQuery</span><span class="o">;</span>
<span class="o">...</span>
<span class="nd">@NamedQueries</span><span class="o">(</span><span class="nx">value</span><span class="o">={</span>
<span class="nd">@NamedQuery</span><span class="o">(</span>
<span class="n">name</span><span class="o">=</span><span class="s">"bugsSummary"</span><span class="o">,</span>
<span class="n">query</span><span class="o">=</span><span class="s">"find (name, email) fetch loggedBugs (title, status) where id=:id "</span><span class="o">),</span>
<span class="nd">@NamedQuery</span><span class="o">(</span>
<span class="n">name</span><span class="o">=</span><span class="s">"bugStatus"</span><span class="o">,</span>
<span class="n">query</span><span class="o">=</span><span class="s">"fetch loggedBugs where loggedBugs.status = :bugStatusorder by name"</span><span class="o">)</span>
<span class="o">})</span>
<span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="nx">name</span><span class="o">=</span><span class="s">"s_user"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">User</span> <span class="kd">implements</span> <span class="n">Serializable</span> <span class="o">{</span>
<span class="o">...</span>
</pre></div>
</div>
<p>You can have named queries, where you define the query. Note that the names of the
queries are per entity type (not global as they are in JPA).</p>
<p>Once you get a named query you set any named parameters and then execute it – in the
case below we use findOne() as we expect only one object graph returned.</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">User</span> <span class="n">u</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">createNamedQuery</span><span class="o">(</span><span class="n">User</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">"bugsSummary"</span><span class="o">)</span>
<span class="o">.</span><span class="na">setParameter</span><span class="o">(</span><span class="s">"id"</span><span class="o">,</span> <span class="mi">1</span><span class="o">)</span>
<span class="o">.</span><span class="na">findOne</span><span class="o">();</span>
</pre></div>
</div>
<h4>Named Queries are Modifyable</h4>
<p>Named queries are parsed early and returned as query objects to you that you can
modify. This means that you can get a named query and then modify the query by adding
to the where clause, setting the order by, limits etc.</p>
<p>This is an intentional feature and means that you can use Named Queries as a "starting
point" to then modify via code and execute.</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// you can treat namedQueries as starting points...</span>
<span class="c1">// ... in that you can modify them via code</span>
<span class="c1">// ... prior to executing the query</span>
<span class="c1">// you can modify a named query...</span>
<span class="n">Set</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">users</span> <span class="o">=</span> <span class="n">DB</span><span class="o">.</span><span class="na">createQuery</span><span class="o">(</span><span class="n">User</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">"bugStatus"</span><span class="o">)</span>
<span class="o">.</span><span class="na">setParameter</span><span class="o">(</span><span class="s">"bugStatus"</span><span class="o">,</span> <span class="s">"NEW"</span><span class="o">)</span>
<span class="c1">// you can add to the where clause</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">ilike</span><span class="o">(</span><span class="s">"name"</span><span class="o">,</span> <span class="s">"rob%"</span><span class="o">)</span>
<span class="c1">// you can set/override the order by</span>
<span class="o">.</span><span class="na">orderBy</span><span class="o">(</span><span class="s">"id desc"</span><span class="o">)</span>
<span class="c1">// you can set/override limits (max rows, first row)</span>
<span class="o">.</span><span class="na">setMaxRows</span><span class="o">(</span><span class="mi">20</span><span class="o">)</span>
<span class="o">.</span><span class="na">findSet</span><span class="o">();</span>
</pre></div>
</div>
<h2 id="large_queries">Large queries</h2>
<p>Large query results can take up a lot of memory if using findList() since loads all the
results in memory at once.
Ebean provides functionality for streaming the results. With this functionality, you
process the results rows one at a time.</p>
<p><b>Query.findIterate()</b> - Execute the query iterating over the results. Requires
calling QueryIterator.close(), typically in a finally block, to prevent resource leakage.
<p><b>Query.findEach(QueryEachConsumer
<T> consumer)
</b> - Execute the query consuming each bean one at a time.
</p>
<o>
This method is appropriate to process very large query results as the beans are consumed
one at a time and do not need to be held in memory (unlike #findList #findSet etc)
</p>
<p>Note that internally Ebean can inform the JDBC driver that it is expecting larger resultSet and
specifically for MySQL this hint is required to stop it's JDBC driver from buffering the entire
resultSet. As such, for smaller resultSets findList() is generally preferable.
</p>
<p>
Compared with findEachWhile() this will always process all the beans where as findEachWhile()
provides a way to stop processing the query result early before all the beans have been read.
</p>
<p>
This method is functionally equivalent to findIterate() but instead of using an iterator uses
the QueryEachConsumer (SAM) interface which is better suited to use with Java8 closures.
</p>
<p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">database</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Customer</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">eq</span><span class="o">(</span><span class="s">"status"</span><span class="o">,</span> <span class="n">Status</span><span class="o">.</span><span class="na">NEW</span><span class="o">)</span>
<span class="o">.</span><span class="na">order</span><span class="o">().</span><span class="na">asc</span><span class="o">(</span><span class="s">"id"</span><span class="o">)</span>
<span class="o">.</span><span class="na">findEach</span><span class="o">((</span><span class="n">Customer</span> <span class="n">customer</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="c1">// do something with customer</span>
<span class="n">System</span><span class="o">.</span><span class="na field">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"-- visit "</span> <span class="o">+</span> <span class="n">customer</span><span class="o">);</span>
<span class="o">});</span>
</pre></div>
</div>
<p><b>Query.findEachWhile(Predicate<T> consumer)
</b> - >Execute the query using callbacks to a visitor to process the resulting beans one at a
time.
</p>
<p>
This is similar to .findEach(...) except that you return boolean true to continue processing
beans and return false to stop processing early.
</p>