forked from ebean-orm/ebean-orm.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoriginal.html
More file actions
967 lines (849 loc) · 54.3 KB
/
original.html
File metadata and controls
967 lines (849 loc) · 54.3 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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta id="layout-head" />
<title>Ebean ORM - Java/Kotlin/JVM</title>
<link rel="shortcut icon" href="/images/favicon.ico" >
<!--<link href="/css/bootstrap.min.css" rel="stylesheet">-->
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/pygments.css" type="text/css" />
<link rel="stylesheet" href="/css/site.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div id="wrap">
<div id="header">
<div class="navbar navbar-inverse navbar-fixed-top" 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/logo-black-3.png" width="32" style="display: inline;">Ebean ORM</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav pull-right">
<li ><a href="/"><span class="glyphicon glyphicon-home"></span> Home</a></li>
<li ><a href="/videos">Videos</a></li>
<li ><a href="/quickstart">Quick Start</a></li>
<li ><a href="/support">Support</a></li>
<li class="active"><a href="/docs">Docs</a></li>
<li ><a target="_blank" href="/apidoc/10">ApiDocs</a></li>
<li ><a href="/releases">Releases</a></li>
<li ><a target="_blank" href="https://github.com/ebean-orm/ebean"> <i class="fa fa-github"></i> Github</a></li>
</ul> </div>
<div class="mobile-nav">
<ul class="nav navbar-nav pull-right">
<li ><a href="/"><span class="glyphicon glyphicon-home"></span> Home</a></li>
<li ><a href="/videos">Videos</a></li>
<li ><a href="/quickstart">Quick Start</a></li>
<li ><a href="/support">Support</a></li>
<li class="active"><a href="/docs">Docs</a></li>
<li ><a target="_blank" href="/apidoc/10">ApiDocs</a></li>
<li ><a href="/releases">Releases</a></li>
<li ><a target="_blank" href="https://github.com/ebean-orm/ebean"> <i class="fa fa-github"></i> Github</a></li>
</ul> </div>
</div>
</div>
</div>
<div id="main" data-offset="60">
<div class="jumbotron mini">
<div class="container">
<h1><a href="/docs">Documentation</a> / <a href="/docs/mapping">Mapping</a> </h1>
</div>
</div>
<div class="container doc-index bs-docs-container">
<div class="row">
<div class="col-md-9" role="main">
<div class="bs-docs-section">
<h1 id="mapping">Mapping</h1>
<p>The main goal of "Mapping" is to isolate the application code from the Database Schema.</p>
<p>This means that some changes can occur to the schema without breaking the application.
The application code can be written without reference to the specific table names, view
names and column names. This means that your application can more easily withstand
some unforseen changes.</p>
<h2 id="jpa_mapping">JPA mapping</h2>
<p>Ebean uses the same mapping as per the JPA specification. You can learn and use the
same mapping annotations. This is generally a very good part of the specification and I'd
expect this part of the specification to mostly stand the test of time.</p>
<h2 id="ddl_generation">DDL generation</h2>
<p>Ebean generated DDL is useful for agile development and testing. It is also useful to help get an understanding of the mapping.</p>
<p>
For simple Databases the DDL generated will be sufficient but for large databases it is not
really 'production quality'. For large Databases you will likely use it as a starting point.
DBA's will want to add more control over physical aspects of Tables and Indexes (specify
tablespaces etc to spread IO across disks, partition large tables, control freespace
depending on the etc).
</p>
<h2 id="naming_convention">Naming convention</h2>
<p>Ebean has a Naming Convention API to map column names to property names. It also
maps entity to table names and can take into account database schema and catalog if
required.</p>
<p>Refer to: com.avaje.ebean.config.NamingConvention</p>
<p>The default UnderscoreNamingConvention converts column names with underscores into
normal java camel case property names (e.g. "first_name" maps to "firstName").</p>
<p>You can also use the MatchingNamingConvention or implement your own.</p>
<p>The MatchingNamingConvention names column names to match property names and table names to match entity names.
Ebean follows the JPA specification which states that the in the case of no annotations the name of the class will be take as the table name and the name of a property will be taken as the name of the column.
</p>
<h2 id="mapping_annotations">Mapping annotations</h2>
<h4>Basics</h4>
<ul>
<li>
@Entity - This simply marks an Entity Bean. Ebean has the same restrictions as per the JPA spec
with the entity beans requiring a default constructor and with properties following the java
beans conventions (with getter and setter names).
</li><li>
@Table - Here you can specify the table name that the entity bean will use. More specifically this is
the "base table" as an entity bean could have a number of "secondary" tables as well.
</li><li>
@Id and @EmbeddedId -Use one of these to mark the property that is the id property. You should use @Id if the id
property is a simple scalar type (like Integer, String etc) and you should use @EmbeddedId if the id type is complex (an embedded bean).
</li><li>
@Column - Use this if the naming convention does not match the bean property name to the database
column or if you need to use quoted identifiers. Otherwise it is not required.
</li><li>
@Lob - This marks a property that is mapped to a Clob/Blob/Longvarchar or Longvarbinary.
</li><li>
@Transient -This marks a property that is not persistent.
</li><li>
@CreatedTimestamp - Sets a timestamp property to current datetie when the entity is created/inserted. An alternative to using this annotation would be to use @Column(insertable = false, updateable = false) and then have the DB insert the current time (set the default value on the DB column is SYSTIME etc). The downside to this alternative approach is that the inserted entity does not have the timestamp value after the insert has occurred. You need to fetch the entity back to get the inserted timestamp if you want to used it.
<br>
@UpdatedTimestamp - Set a timestamp property that is set to the datetime when the entity was last updated.
</li>
</ul>
<h2 id="relationships">Relationships</h2>
<h4>Database Design and Normalisation</h4>
<p>If you are familiar with Database design and normalisation then the relationships
will become clear fairly quickly. If you are not familiar then it is recommended that you read up on these
topics (a quick trip to wikipedia) since it will help you a lot in this area of ORM mapping.
</p>
<h4>DB Foreign Keys and ORM Relationships</h4>
<p>Assuming your DB has foreign keys and has been well designed then the ORM mapping
should follow quite naturally. If you DB has a more "interesting" design then the ORM
mapping can be a lot more painful with more compromises.</p>
<h4>One-to-Many relationship</h4>
<p>This is probably the most common relationship so we will start with a One to Many
relationship. The @OneToMany and the @ManyToOne represent the two ends of a
relationship. This relationship typically maps directly to a Database Foreign Key
constraint...</p>
<h4>Database Foreign Key constraint</h4>
<p>A typical database design is full of "One to Many/Many to One" relationships implemented
using a Foreign Key constraint. A foreign key constraint has an "imported" side and an
"exported" side.</p>
<ul>
<li>"A Customer has many Orders"</li>
<li>"An Order belongs to a Customer"<li>
<li>The customer table "exports" its primary key to the order table</li>
<li>The order table "imports/references" the customer table's primary key.</li>
</ul>
<div class="bs-callout bs-callout-primary">
<p> A Foreign Key constraint can be viewed from the exported side
(customer table) or the imported side (order table).</p>
<p>... @OneToMany and @ManyToOne map directly to this.</p>
</div>
<p>
The Customer entity bean...
<div class="syntax java"><div class="highlight"><pre><span></span><span class="o">...</span>
<span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"or_customer"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Customer</span> <span class="o">{</span>
<span class="o">...</span>
<span class="nd">@OneToMany</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>
</pre></div>
</div>
The Order entity bean...
<div class="syntax java"><div class="highlight"><pre><span></span><span class="o">...</span>
<span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"or_order"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Order</span> <span class="o">{</span>
<span class="o">...</span>
<span class="nd">@ManyToOne</span>
<span class="n">Customer</span> <span class="n">customer</span><span class="o">;</span>
</pre></div>
</div>
</p>
<p>
Because the @OneToMany and @ManyToOne are both mapped this is a
"Bidirectional" relationship. You can navigate the object graph in either
direction.
</p>
<h4>Unidirectional Relationships</h4>
<p>To turn a Bidirectional relationship into a Unidirectional relationship you need to either
remove the @OneToMany (Customer.orders property) or the @ManyToOne (Order.customer property).</p>
<h4>Removing a OneToMany - no problem</h4>
<p>eg. Remove List<Order> from Customer
</p><p>
You can generally remove the OneToMany side of a relationship without any major issues.
The issue being that you can not navigate the object graph in that direction.
</p><p>
Why remove a OneToMany? Sometimes the OneToMany side is not useful to the
application or even dangerous if used. For example on Product there could be a
List<OrderDetail> but that could be considered useless or even dangerous if it was
navigated (and all order details for a product where lazy loaded).
<h4>Removing a ManyToOne - watch those inserts...</h4>
<p>eg. Remove Customer from Order
</p><p>
If you remove a ManyToOne this effects how a bean is saved (specifically the insert). The
reason is because it is the ManyToOne (importing) side of the relationship that holds the
foreign key column (e.g. or_order table holds the customer_id foreign key column).
</p><p>
Q: If the customer property is removed from the Order object how would you specify
which customer placed an order when you create a new order?
In Database speak this translates to ... when inserting an order how is the customer_id
column populated?
</p><p>
A: You have to use cascading save on the customer.orders and save the customer.
Sounds like a pain... and it would be in this case... lets look at a more realistic case where
you want to remove a ManyToOne...
</p><p>
eg. Remove Order from OrderDetail
</p><p>
Lets say you remove the Order property from the OrderDetail bean. Now lets say you
want to write some code to add a OrderDetail to an Order (insert). How do you specify
which Order it should go to?
</p><p>
"Turn on" cascade save on the @OneToMany side
<div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"or_order"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Order</span> <span class="o">{</span>
<span class="o">...</span>
<span class="c1">// must cascade the save</span>
<span class="nd">@OneToMany</span><span class="o">(</span><span class="n">cascade</span><span class="o">=</span><span class="n">CascadeType</span><span class="o">.</span><span class="na">ALL</span><span class="o">)</span>
<span class="n">List</span><span class="o"><</span><span class="n">OrderDetail</span><span class="o">></span> <span class="n">details</span><span class="o">;</span>
</pre></div>
</div>
And save the order... which cascade saves the details
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// create or fetch the order</span>
<span class="n">Order</span> <span class="n">order</span> <span class="o">=</span> <span class="o">...</span>
<span class="n">List</span><span class="o"><</span><span class="n">OrderDetail</span><span class="o">></span> <span class="n">details</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o"><</span><span class="n">OrderDetail</span><span class="o">>();</span>
<span class="n">OrderDetail</span> <span class="n">orderDetail</span> <span class="o">=</span> <span class="o">...</span>
<span class="n">details</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">orderDetail</span><span class="o">);</span>
<span class="c1">// set the new details...</span>
<span class="n">order</span><span class="o">.</span><span class="na">setDetails</span><span class="o">(</span><span class="n">details</span><span class="o">);</span>
<span class="c1">// save the order... which cascade saves</span>
<span class="c1">// the order details...</span>
<span class="n">Ebean</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">order</span><span class="o">);</span>
</pre></div>
</div>
</p><p>
So when the order is saved, because the @OneToMany relationship has cascade.ALL
the save is cascaded to all the order details.
</p><p>
Note that you can update OrderDetails individually (without relying on cascade save) but
to insert a new OrderDetail we are relying on the cascading save.
<div class="bs-callout bs-callout-primary">
<p>Removing a ManyToOne typically reflects a strong "ownership"
relationship. The Order "owns" the OrderDetails, they are persisted as
one via cascade save.</p>
</div>
<h4>Managed Relationships = @OneToMany + cascade save</h4>
<p>
If cascade save is on a @OneToMany when the save is cascaded down from the 'master'
to the 'details' Ebean will 'manage' the relationship.
</p><p>
For example, with the Order - OrderDetails relationship when you save the order Ebean
will get the order id and make sure it is 'set' on all the order details. Ebean does this for
both Bidirectional relationships and Unidirectional relationships.
</p><p>
What this means is that if your OrderDetails has an @ManyToOne Order property (its
bidirectional) you do not need to set the order against each orderDetail when you use
cascade save. Ebean will automatically set the 'master' order to each of the details when
you save the Order and that cascades down to the details.
</p>
<h4>@OneToMany Notes</h4>
<p>
When you assign a @OneToMany you typically specify a mappedBy attribute. This is for
Bi-directional relationships and in this case the "join" information is read from the other
side of the relationship (meaning you don't specify any @JoinColumn etc on this side).
</p><p>
If you don't have a mappedBy attribute (there is no matching property on the other related
bean) then this is a Unidirectional relationship. In this case you can specify a
<div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@JoinColumn</span> <span class="k">if</span> <span class="n">you</span> <span class="n">wish</span> <span class="n">to</span> <span class="n">override</span> <span class="n">the</span> <span class="n">join</span> <span class="n">column</span> <span class="n">information</span> <span class="n">from</span> <span class="n">the</span> <span class="k">default</span><span class="o">).</span>
<span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">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="c1">// unidirectional …</span>
<span class="c1">// … can explicitly specify the join column if needed</span>
<span class="nd">@OneToMany</span>
<span class="nd">@JoinColumn</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"pref_id"</span><span class="o">)</span>
<span class="n">List</span><span class="o"><</span><span class="n">Preference</span><span class="o">></span> <span class="n">preferences</span><span class="o">;</span>
<span class="c1">// bi-directional</span>
<span class="c1">// … join information always read from the other side</span>
<span class="nd">@OneToMany</span><span class="o">(</span><span class="n">mappedBy</span><span class="o">=</span><span class="s">"userLogged"</span><span class="o">)</span>
<span class="n">List</span><span class="o"><</span><span class="n">Bug</span><span class="o">></span> <span class="n">loggedBugs</span><span class="o">;</span>
</pre></div>
</div>
<h4>One-to-One relationship</h4>
<p>
A One-to-One relationship is exactly the same as a One-to-Many relationship except that
the many side is limited to a maximum of one.
</p><p>
That means that one of the @OneToOne sides operates just like a @ManyToOne (the
imported side with the foreign key column) and the other @OneToOne operates just like a
@OneToMany (exported side).
</p><p>
So you put the mappedBy on the 'exported side' – as if it was a @OneToMany.
</p><p>
From a Database perspective a One to One relationship is implemented with a foreign key
constraint (like one to many) and adding a unique constraint on the foreign key column.
This has the effect of limiting the "many" side to a maximum of one (has to be unique).
<h4>Many-to-Many relationship</h4>
<p>
You are probably aware that there are no Many to Many relationships in a physical
database design. These are implemented with an intersection table and two One to Many
relationships.
</p><p>
Lets look at an example...
<ul>
<li>A User can have many Roles</li>
<li>A Role can be assigned to many Users</li>
<li>A Many to Many between user and role</li>
</ul>
<img src="/images/docs/A_Many_to_Many_between_user_and_role.png" class="img-responsive">
<p>
In the database diagram above there is an intersection table called s_user_role. This
represents a logical many to many relationship between user and role.
</p><p>
Q: When is a Many to Many better represented as two One to Many relationships?
</p><p>
A: If there is ever an additional column in the intersection table then you should consider
changing this from a Many to Many to two One to Many's and including the intersection
table in the model.
</p><p>
One way to think of this is that each @ManyToMany operates just like it was a
@OneToMany. The relationship must be "managed" meaning Ebean must take care of
inserting into and deleting from the intersection table.
</p><p>
The way this works is that any additions or removables from the many list/set/map are
noted. These become inserts into and deletes from the intersection table.
<div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">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>
<span class="nd">@ManyToMany</span><span class="o">(</span><span class="n">cascade</span><span class="o">=</span><span class="n">CascadeType</span><span class="o">.</span><span class="na">ALL</span><span class="o">)</span>
<span class="n">List</span><span class="o"><</span><span class="n">Role</span><span class="o">></span> <span class="n">roles</span><span class="o">;</span>
<span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"s_role"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Role</span> <span class="o">{</span>
<span class="o">...</span>
<span class="nd">@ManyToMany</span><span class="o">(</span><span class="n">cascade</span><span class="o">=</span><span class="n">CascadeType</span><span class="o">.</span><span class="na">ALL</span><span class="o">)</span>
<span class="n">List</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">users</span><span class="o">;</span>
</pre></div>
</div>
</p><p>
The intersection table name and foreign key columns can default or be specified by @JoinTable etc.
</p><p>
The following code shows a new role added to a user.
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">User</span> <span class="n">user</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">User</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="mi">1</span><span class="o">);</span>
<span class="n">List</span><span class="o"><</span><span class="n">Role</span><span class="o">></span> <span class="n">roles</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="na">getRoles</span><span class="o">();</span>
<span class="n">Role</span> <span class="n">role</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">Role</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="mi">27</span><span class="o">);</span>
<span class="c1">// adding a role to the list...this is remembered and will</span>
<span class="c1">// result in an insert into the intersection table</span>
<span class="c1">// when save cascades...</span>
<span class="n">roles</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">role</span><span class="o">);</span>
<span class="c1">// save cascades to roles... and in this case</span>
<span class="c1">// results in an insert into the intersection table</span>
<span class="n">Ebean</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</pre></div>
</div>
</p><p>
Note that if a role was removed from the list this would result in an appropriate delete from
the intersection table.
</p>
<h2 id="id_generation">Id generation</h2>
<ul>
<li>DB Identity / Autoincrement</li>
<li>DB Sequences</li>
<li>UUID</li>
<li>Custom ID Generation</li>
</ul>
<p>There are 4 ways that ID's can be automatically generated for new Entities. This occurs
when a entity is going to be inserted and it does not already have an Id value.</p>
<p>The first 3 options are highly recommended for 2 reasons.</p>
<ol>
<li>They are standard approaches that can also be used by other programs, stored
procedures, batch loading jobs etc that could be written in other languages etc. That is, if
you choose a custom ID Generation then this can make it more difficult to use other
programs / tools to insert into the DB.</li>
<li>They support good concurrency – can you really do better? Most Databases support Sequences or Identity/Autoincrement. DB2 and H2 support both.</li>
</ol>
<h4>UUID Id Generation</h4>
<p>To use UUID's with Ebean all you need to do is use the UUID type for your id property.</p>
<p>Ebean will automatically assign an appropriate UUID Id generator.
<div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Entity</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyEntity</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="n">UUID</span> <span class="n">id</span><span class="o">;</span>
<span class="o">...</span>
</pre></div>
</div>
</p>
<h4>DB Sequences / DB Autoincrement</h4>
<p>
Refer: com.avaje.ebean.config.dbplatform.DatabasePlatform and DbIdentity
</p>
<p>
For each database type (Oracle, MySql, H2, Postgres etc) there is a specific
DatabasePlatform which defines whether the database supports sequences or
autoincrement. This then defines whether DB sequences or DB Identity / Autoincrement
will be used. This also provides a sequence generator specific to that database.
</p><p>
For DB sequences the NamingConvention is used to define the default name of the
sequences. This name will be used unless the sequence name is explicitly defined via
annotations.
</p><p>
What this means is that, typically you only need to the the @Id annotation unless you
need to override a sequence name (when it doesn't match the naming convention).
<div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Entity</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyEntity</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="n">Integer</span> <span class="n">id</span><span class="o">;</span>
<span class="o">...</span>
</pre></div>
</div>
</p>
<h4>Batched fetch of Db Sequences</h4>
<p>For performance reasons we don't want to fetch a sequence value each time we want an
Id. Instead we fetch a 'batch' of sequences (refer to ServerConfig
setDatabaseSequenceBatchSize() ) - the default batch size is 20.
</p><p>
Also note that when the number of available Id's for a given sequence drops to half the
batch size then another batch of sequences is fetched via a background thread.
</p><p>
For Oracle, Postgres and H2 we use Db sequences. It is worth noting that this allows the
use of JDBC batch statements (PreparedStatement.addBatch() etc) which is a significant
performance optimization. You can globally turn on the use of JDBC batching via
ServerConfig.setUsePersistBatching() … or you can turn it on for a specific Transaction.
</p>
<h2 id="formula">@Formula</h2>
<p>
@Formula can be used to get read only values using SQL Literals, SQL Expressions and SQL Functions.
</p><p>
With a Formula the $\{ta} is a special token to represent the table alias. The table alias is
dynamically determined by Ebean and you can put the $\{ta} in the select or join attributes.
</p>
<h4>A SQL Expression</h4>
<p>
Example: The caseForm field using a SQL case expression
<div class="syntax java"><div class="highlight"><pre><span></span><span class="o">...</span>
<span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">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="o">{</span>
<span class="nd">@Id</span>
<span class="n">Integer</span> <span class="n">id</span><span class="o">;</span>
<span class="nd">@Formula</span><span class="o">(</span><span class="n">select</span><span class="o">=</span><span class="s">"(case when $\{ta}.id > 4 then 'T' else 'F' end)"</span><span class="o">)</span>
<span class="kt">boolean</span> <span class="n">caseForm</span><span class="o">;</span>
<span class="o">...</span>
</pre></div>
</div>
</p><p>
Note the $\{ta} in place of the table alias
</p><p>
Note in this deployment 'T' and 'F' are mapped to boolean values.
</p>
<h4>A SQL Function</h4>
<p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Formula</span><span class="o">(</span><span class="n">select</span><span class="o">=</span><span class="s">"(select count(*) from f_topic _b where _b.user_id =$\{ta}.id)"</span><span class="o">)</span>
<span class="kt">int</span> <span class="n">countAssiged</span><span class="o">;</span>
</pre></div>
</div>
</p><p>
The formula properties can be used as normal properties. This includes in query select
and where expressions.
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// include the countAssigned property</span>
<span class="n">Query</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">query</span> <span class="o">=</span> <span class="n">Ebean</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="n">query</span><span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"id, name, countAssiged"</span><span class="o">);</span>
<span class="n">query</span><span class="o">.</span><span class="na">fetch</span><span class="o">(</span><span class="s">"topics"</span><span class="o">);</span>
<span class="n">List</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
</p><p>
The SQL generated from the query above is:
<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.User]'</span><span class="nt">></span>
select u.id, u.name,
(select count(*) from f_topic _b where _b.user_id = u.id)
from s_user u
<span class="nt"></sql></span>
</pre></div>
</div>
</p><p>
Note the "u" in the sql has replaced the $\{ta} [table alias placeholder] specified in the
select attribute of the formula.
</p><p>
<b>It is also worth noting that this is potentially not great SQL!!!</b> You should check SQL
in this form (get the explain plan for the query – get your DBA to review the sql etc) but
there is a good chance the sub query (select count(*) ... _b.user_id = u.id) will effectively
execute for each row returned. If that is the case the query above can quickly become
expensive and you may find you have an unhappy DBA .
</p><p>
The above can be re-written to use a view (if one exists). The benefit is that we can use a
join rather than a subquery which can perform much better from a database perspective.
<div class="syntax sql"><div class="highlight"><pre><span></span><span class="o">//</span> <span class="k">VIEW</span><span class="p">:</span> <span class="n">vw_topic_aggr</span>
<span class="o">//</span> <span class="n">Lets</span> <span class="n">say</span> <span class="n">there</span> <span class="k">is</span> <span class="n">a</span> <span class="k">view</span> <span class="n">base</span> <span class="k">on</span> <span class="n">this</span> <span class="k">SQL</span><span class="p">.</span>
<span class="o">//</span> <span class="n">It</span> <span class="k">is</span> <span class="n">typically</span> <span class="k">more</span> <span class="n">performant</span> <span class="k">to</span> <span class="k">JOIN</span>
<span class="o">//</span> <span class="k">to</span> <span class="n">a</span> <span class="k">view</span> <span class="n">rather</span> <span class="k">than</span> <span class="n">use</span> <span class="n">a</span> <span class="n">subquery</span>
<span class="k">create</span> <span class="k">view</span> <span class="n">vw_topic_aggr</span> <span class="k">as</span>
<span class="k">select</span> <span class="n">user_id</span><span class="p">,</span> <span class="k">max</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="k">as</span> <span class="n">topic_max</span><span class="p">,</span> <span class="k">count</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">as</span> <span class="n">topic_count</span>
<span class="k">from</span> <span class="n">f_topic</span>
<span class="k">group</span> <span class="k">by</span> <span class="n">user_id</span>
</pre></div>
</div>
And use the join attribute of @Formula
<div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Formula</span><span class="o">(</span>
<span class="n">select</span><span class="o">=</span><span class="s">"_b$\{ta}.topic_count"</span><span class="o">,</span>
<span class="n">join</span><span class="o">=</span><span class="s">"join vw_topic_aggr as _b$\{ta} on _b$\{ta}.user_id = id"</span><span class="o">)</span>
<span class="kt">int</span> <span class="n">countWithJoin</span><span class="o">;</span>
</pre></div>
</div>
</p><p>
Now, if the view does not exist we can do something similar ...
</p><p>
In this next @Formula the join attribute contains the select effectively replacing the
vw_topic_aggr view with (select user_id, max(id) as topic_max, count(*) as topic_count
from f_topic group by user_id).
@Formula(
select="_b$\{ta}.topic_count",
join="join (select user_id, max(id) as topic_max, count(*) as topic_count from f_topic group by user_id) as _b$\{ta} on _b$ {ta}.user_id = id")
int countWithJoin;
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">Query</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">query</span> <span class="o">=</span> <span class="n">Ebean</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="n">query</span><span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"id, name, countWithJoin"</span><span class="o">);</span>
<span class="n">List</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
</p><p>
Results in the following SQL: - will generally perform better than the subquery
<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.User]'</span><span class="nt">></span>
select u.id, u.name, _bu.topic_count
from s_user u
join (select user_id, max(id) as topic_max, count(*) as topic_count
from f_topic group by user_id) as _bu on _bu.user_id = id
<span class="nt"></sql></span>
</pre></div>
</div>
</p><p>
It is also worth noting that the formula fields can be used in where expressions.
</p><p>
Example: where countWithJoin > 1
<div class="syntax java"><div class="highlight"><pre><span></span><span class="n">Query</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">query</span> <span class="o">=</span> <span class="n">Ebean</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="n">query</span><span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="s">"id, name, countWithJoin"</span><span class="o">);</span>
<span class="c1">// using the formula field in the where</span>
<span class="n">query</span><span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">gt</span><span class="o">(</span><span class="s">"countWithJoin"</span><span class="o">,</span> <span class="mi">1</span><span class="o">);</span>
<span class="n">List</span><span class="o"><</span><span class="n">User</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="na">findList</span><span class="o">();</span>
</pre></div>
</div>
Resulting 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.User]'</span><span class="nt">></span>
select u.id, u.name, _bu.topic_count
from s_user u
join (select user_id, max(id) as topic_max, count(*) as topic_count
from f_topic group by user_id) as _bu on _bu.user_id = id
where _bu.topic_count > ?
<span class="nt"></sql></span>
</pre></div>
</div>
</p>
<h2 id="enummapping">EnumMapping</h2>
<p>
This is an Ebean specific annotation (not part of JPA) for mapping Enum's to database
values. The reason it exists is that IMO the JPA approach for mapping of Enums is highly
dangerous (in the case of Ordinal mapping) or not very practical (in the case of String
mapping).
</p><p>
Lets take the example of this Enumeration:
<div class="syntax java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">enum</span> <span class="n">UserStatus</span> <span class="o">{</span>
<span class="n">ACTIVE</span><span class="o">,</span> <span class="n">INACTIVE</span><span class="o">,</span> <span class="n">NEW</span>
<span class="o">}</span>
</pre></div>
</div>
</p><p>
Enum Ordinal Mapping is Dangerous
</p><p>
In my opinion JPA Ordinal Mapping for Enum's is very dangerous, and it is recommended that you avoid it.
The reason is because the ordinal values for Enum depends on the order in which they appear.
<div class="syntax java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">TestStatus</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">ord0</span> <span class="o">=</span> <span class="n">UserStatus</span><span class="o">.</span><span class="na field">ACTIVE</span><span class="o">.</span><span class="na">ordinal</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">ord1</span> <span class="o">=</span> <span class="n">UserStatus</span><span class="o">.</span><span class="na field">INACTIVE</span><span class="o">.</span><span class="na">ordinal</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">ord2</span> <span class="o">=</span> <span class="n">UserStatus</span><span class="o">.</span><span class="na field">NEW</span><span class="o">.</span><span class="na">ordinal</span><span class="o">();</span>
<span class="c1">// 0, 1, 2</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">"ord 0:"</span><span class="o">+</span><span class="n">ord0</span><span class="o">+</span><span class="s">" 1:"</span><span class="o">+</span><span class="n">ord1</span><span class="o">+</span><span class="s">" 2:"</span><span class="o">+</span><span class="n">ord2</span><span class="o">);</span>
<span class="n">String</span> <span class="n">str0</span> <span class="o">=</span> <span class="n">UserStatus</span><span class="o">.</span><span class="na field">ACTIVE</span><span class="o">.</span><span class="na">name</span><span class="o">();</span>
<span class="n">String</span> <span class="n">str1</span> <span class="o">=</span> <span class="n">UserStatus</span><span class="o">.</span><span class="na field">INACTIVE</span><span class="o">.</span><span class="na">name</span><span class="o">();</span>
<span class="n">String</span> <span class="n">str2</span> <span class="o">=</span> <span class="n">UserStatus</span><span class="o">.</span><span class="na field">NEW</span><span class="o">.</span><span class="na">name</span><span class="o">();</span>
<span class="c1">// "ACTIVE", "INACTIVE", "NEW"</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">"str 0:"</span><span class="o">+</span><span class="n">str0</span><span class="o">+</span><span class="s">" 1:"</span><span class="o">+</span><span class="n">str1</span><span class="o">+</span><span class="s">" 2:"</span><span class="o">+</span><span class="n">str2</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div>
</div>
OUTPUT:
<pre>
ord 0:0 1:1 2:2
str 0:ACTIVE 1:INACTIVE 2:NEW
</pre>
</p><p>
The problem is that if you change the order of the Enum elements such as in this example
(DELETED is now first with Ordinal value of 0) ...
<div class="syntax java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">enum</span> <span class="n">UserStatus</span> <span class="o">{</span>
<span class="n">DELETED</span><span class="o">,</span> <span class="n">ACTIVE</span><span class="o">,</span> <span class="n">INACTIVE</span><span class="o">,</span> <span class="n">NEW</span>
<span class="o">}</span>
</pre></div>
</div>
</p><p>
With the above code the Ordinal values for ACTIVE, INACTIVE and NEW have all
changed. This is a very subtle change and now every status existing in the database will
be incorrectly represented in the application. Hopefully this issue would be picked up
quickly but there could be situations where this subtle data issue is not picked up before a
real disaster has occured.
<h4>Enum String mapping is limited</h4>
<p>It is more likely that your DBA would prefer to save space by mapping this to a
VARCHAR(1) column and use "A", "I", "N" and "D" as codes to represent ACTIVE,
INACTIVE, NEW and DELETED.
</p><p>
The issue with the String mapping is that more frequently than not the names of the
Enumeration elements will have to be comprimised to short less-meaningful names to
map into DB values or your DBA will be unhappy with long wasteful values.
</p>
<h4>@EnumValue</h4>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">enum</span> <span class="n">UserStatus</span> <span class="o">{</span>
<span class="nd">@EnumValue</span><span class="o">(</span><span class="s">"D"</span><span class="o">)</span> <span class="n">DELETED</span><span class="o">,</span>
<span class="nd">@EnumValue</span><span class="o">(</span><span class="s">"A"</span><span class="o">)</span> <span class="n">ACTIVE</span><span class="o">,</span>
<span class="nd">@EnumValue</span><span class="o">(</span><span class="s">"I"</span><span class="o">)</span> <span class="n">INACTIVE</span><span class="o">,</span>
<span class="nd">@EnumValue</span><span class="o">(</span><span class="s">"N"</span><span class="o">)</span> <span class="n">NEW</span>
<span class="o">}</span>
</pre></div>
</div>
<p>
With EnumValue (Ebean specific annotation) you explicitly specify the value map the entry
to. This Annotation has been logged with the Eclipselink project in the hope it makes it's
way into the JPA spec.
</p>
<h2 id="encryption">Encryption</h2>
<h3>Transparent Encryption with Ebean</h3>
<p>
Ebean v2.4 added support for automatic Encryption/Decryption of properties.
The result is that without changing your application code some of your
properties on your entities can stored in encrypted form in the DB.
</p>
<h3>DB Encryption and "Java client Encryption"</h3>
<ul>
<li>The encryption can be performed by Database functions or on the Java side (Java client encryption).</li>
<li>
Properties that are encrypted using Database functions can be used in Query WHERE clauses.
</li>
<li>
Properties that are encrypted using "Java client Encryption" can NOT be used in Query WHERE clauses.
</li>
</ul>
<h3>DB Encryption of String and "Date" types</h3>
<p>
Currently DB Encryption for H2, Postgres, MySql and Oracle can be used on String types and "Date" types (java.sql.Date, Joda DateMidnight, Joda LocalDate).
</p>
<p>
Any type not supported by DB Encryption functions uses "Java client Encryption".
</p>
<h3>DB Encryption Functions</h3>
<p>
The default DB encryption decryption functions used are:
</p>
<ul>
<li>H2: ENCRYPT() and DECRYPT() with 'AES' option</li>
<li>MySql: AES_ENCRYPT() and AES_DECRYPT()</li>
<li>Postgres: requires pgcryto and uses PGP_SYM_ENCRYPT() and PGP_SYN_DECRYPT()</li>
<li>Oracle: requires dbms_crypto and uses custom functions for encryption and decryption</li>
</ul>
<h3>Encryptor (Java client encryption implementation)</h3>
<p>
When Java client encryption occurs the "Encryptor" interface is used. By default Ebean has a AES 128 bit based implementation but you can also configure Ebean to use your own implementation if you wish.
</p>
<h3>EncryptKeyManager</h3>
<p>
Whenever a property is encrypted or decrypted a "Key" must be used. Ebean will internally ask the EncryptKeyManager for a key given the table and column name.
</p>
<p>
You must supply an implementation of the EncryptKeyManager.
</p>
<h3>Deployment: @Encrypted and EncryptDeployManager</h3>
<p>
You can mark a property to be Encrypted programmatically via EncryptDeployManager, or put the @Encrypted annotation on the bean property or a combination of the two.
</p>
<p>
By default a property will use DB encryption if it is supported (based on its type - String or 'Dates' basically). If you want to ensure a property uses Java encryption you can specify dbEncryption=false on the annotation or EncryptDeploy. You may want to do this so that the CPU cycles for encryption/decryption occur on the Java process rather than the DB process.
</p>
<p>
Your application code does not change
That's it. Your code to query, insert, update and delete etc does not change and Ebean automatically does the encryption/decryption behind the scenes.
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// Use @Encrypted annotation to mark the encrypted properties</span>
<span class="o">...</span>
<span class="nd">@Entity</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"pm_patient"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Patient</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="n">Integer</span> <span class="n">id</span><span class="o">;</span>
<span class="nd">@Encrypted</span>
<span class="n">String</span> <span class="n">name</span><span class="o">;</span>
<span class="c1">// use client side encryption (not db functions)</span>
<span class="nd">@Lob</span>
<span class="nd">@Encrypted</span><span class="o">(</span><span class="n">dbEncryption</span><span class="o">=</span><span class="kc">false</span><span class="o">)</span>
<span class="n">String</span> <span class="n">description</span><span class="o">;</span>
<span class="nd">@Encrypted</span>
<span class="n">Date</span> <span class="n">dob</span><span class="o">;</span>
<span class="o">...</span>
</pre></div>
</div>
<h3>Limitations</h3>
<ul>
<li>
Properties using Java client encryption can NOT be used in WHERE clauses
</li>
<li>
Only DB Encryption support built in for H2, Postgres, MySql and Oracle.
</li>
<li>
You can not use Encryption with positioned (1,2,3...) parameters. You must
use named parameters or the criteria api to define queries.
</li>
</ul>
<h3>Examples:</h3>
List<Patient> list =
Ebean.find(Patient.class)
.select("id, name")
.where().eq("name", "Rob")
.findList();
<p>
Results in the following SQL:
</p>
<div class="syntax xml"><div class="highlight"><pre><span></span><span class="nt"><sql</span> <span class="na">summary=</span><span class="s">'Patient'</span> <span class="nt">></span>
select e.id as c0, pgp_sym_decrypt(e.name,?) as c1
from pm_patient e
where pgp_sym_decrypt(e.description,?) = ?
<span class="nt"><sql></span>
</pre></div>
</div>
<h3>Configuraton:</h3>
<p>
You can specify the EncryptKeyManager implementation in the ebean.properties
file like below:
</p>
<div class="syntax properties"><div class="highlight"><pre><span></span><span class="c"># ebean.properties - specify the Key Manager</span>
<span class="na">ebean.encryptKeyManager</span><span class="o">=</span><span class="s">com.avaje.tests.basic.encrypt.BasicEncyptKeyManager</span>
</pre></div>
</div>
<p>
Or if you are programmatically configuring an EbeanServer using ServerConfig,
set the EncryptKeyManager.
</p>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="c1">// programmatically configure an EbeanServer</span>
<span class="c1">// with an EncryptKeyManager</span>
<span class="n">ServerConfig</span> <span class="n">config</span> <span class="o">=</span> <span class="n">ServerConfig</span><span class="o">();</span>
<span class="o">...</span>
<span class="n">EncryptKeyManager</span> <span class="n">keyManager</span> <span class="o">=</span> <span class="o">...;</span>
<span class="n">config</span><span class="o">.</span><span class="na">setEncryptKeyManager</span><span class="o">(</span><span class="n">encyptKeyManager</span><span class="o">());</span>
<span class="o">...</span>
<span class="n">EbeanServer</span> <span class="n">server</span> <span class="o">=</span> <span class="n">EbeanServerFactory</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">config</span><span class="o">);</span>
</pre></div>
</div>
<h3>A EncryptKeyManager:</h3>
<div class="syntax java"><div class="highlight"><pre><span></span><span class="kn">package</span> <span class="nn">com.avaje.tests.basic.encrypt</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.avaje.ebean.config.EncryptKey</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.avaje.ebean.config.EncryptKeyManager</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">BasicEncyptKeyManager</span> <span class="kd">implements</span> <span class="n">EncryptKeyManager</span> <span class="o">{</span>
<span class="cm">/**</span>
<span class="cm"> * Initialise the key manager.</span>
<span class="cm"> */</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">initialise</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">// can load keys or initialise source resources ...</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">EncryptKey</span> <span class="nf">getEncryptKey</span><span class="o">(</span><span class="n">String</span> <span class="n">tableName</span><span class="o">,</span> <span class="n">String</span> <span class="n">columnName</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// get the key for the given table and column</span>
<span class="n">String</span> <span class="n">keyValue</span> <span class="o">=</span> <span class="o">...;</span>
<span class="k">return</span> <span class="k">new</span> <span class="n">BasicEncryptKey</span><span class="o">(</span><span class="n">keyValue</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div>
</div>
<h3>Internals</h3>
<p>
What Ebean is doing internally is detecting when an encrypted
property is being used. Internally it will call the EncryptKeyManager
with the table and column of the property to get the encryption key.
This key is then added as a bind variable to the prepared statement.
</p>
<p>
As the key is added as a bind variable into the statement you can
not use encryption with 'ordered' parameters because it can effectively
change the position of other parameters. You can use 'named'
parameters or the criteria api for building queries - but you can't
use positioned (1,2,3,4..) parameters.
</p>
</div>
</div>
<div class="col-md-3" role="complementary">
<nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm">
<ul class="nav bs-docs-sidenav">
<li >
<a href="/docs/mapping">Overview</a>
</li>
<li >
<a href="/docs/mapping/naming-convention">Naming convention</a>
</li>
<li >
<a href="/docs/mapping/constructors">Constructors</a>
</li>
<li >
<a href="/docs/mapping/list">Collections</a>
</li>
<li >
<a href="/docs/mapping/jpa">JPA Mapping</a>
</li>
<li >
<a href="/docs/mapping/extension">Extensions</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
<footer id="footer">
<div class="doc-footer">
<ul class="doc-footer-links">
<li><a href="/">Ebean ORM</a></li>
<li> | </li>
<li><a href="http://avaje-metric.github.io">Metrics</a></li>
</ul>
</div>
</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script src="/js/jquery.easing.min.js"></script>
<script src="/js/json2.js"></script>
<script src="/js/jquery.cookie.min.js"></script>
<script src="/js/jquery.storageapi.min.js"></script>
<script src="/js/ebean-site.js"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-75181644-1', 'auto');
ga('send', 'pageview');
</script> </body>
</html>