/
Envers.xml
1725 lines (1549 loc) · 82.6 KB
/
Envers.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version='1.0' encoding='utf-8' ?>
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" >
<info>
<title>Envers</title>
<abstract>
<para>
The aim of Hibernate Envers is to provide historical versioning of your application's entity data. Much
like source control management tools such as Subversion or Git, Hibernate Envers manages a notion of revisions
if your application data through the use of audit tables. Each transaction relates to one global revision number
which can be used to identify groups of changes (much like a change set in source control). As the revisions
are global, having a revision number, you can query for various entities at that revision, retrieving a
(partial) view of the database at that revision. You can find a revision number having a date, and the other
way round, you can get the date at which a revision was committed.
</para>
</abstract>
</info>
<section>
<title>Basics</title>
<para>
To audit changes that are performed on an entity, you only need two things: the
<literal>hibernate-envers</literal> jar on the classpath and an <literal>@Audited</literal> annotation
on the entity.
</para>
<important>
<para>
Unlike in previous versions, you no longer need to specify listeners in the Hibernate configuration
file. Just putting the Envers jar on the classpath is enough - listeners will be registered
automatically.
</para>
</important>
<para>
And that's all - you can create, modify and delete the entities as always. If you look at the generated
schema for your entities, or at the data persisted by Hibernate, you will notice that there are no changes.
However, for each audited entity, a new table is introduced - <literal>entity_table_AUD</literal>,
which stores the historical data, whenever you commit a transaction. Envers automatically creates audit
tables if <literal>hibernate.hbm2ddl.auto</literal> option is set to <literal>create</literal>,
<literal>create-drop</literal> or <literal>update</literal>. Otherwise, to export complete database schema
programatically, use <literal>org.hibernate.tool.EnversSchemaGenerator</literal>. Appropriate DDL
statements can be also generated with Ant task described later in this manual.
</para>
<para>
Instead of annotating the whole class and auditing all properties, you can annotate
only some persistent properties with <literal>@Audited</literal>. This will cause only
these properties to be audited.
</para>
<para>
The audit (history) of an entity can be accessed using the <literal>AuditReader</literal> interface, which
can be obtained having an open <literal>EntityManager</literal> or <literal>Session</literal> via
the <literal>AuditReaderFactory</literal>. See the javadocs for these classes for details on the
functionality offered.
</para>
</section>
<section>
<title>Configuration</title>
<para>
It is possible to configure various aspects of Hibernate Envers behavior, such as table names, etc.
</para>
<table frame="topbot">
<title>Envers Configuration Properties</title>
<tgroup cols="3">
<colspec colname="c1" colwidth="1*"/>
<colspec colname="c2" colwidth="1*"/>
<colspec colname="c2" colwidth="1*"/>
<thead>
<row>
<entry>Property name</entry>
<entry>Default value</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<property>org.hibernate.envers.audit_table_prefix</property>
</entry>
<entry>
</entry>
<entry>
String that will be prepended to the name of an audited entity to create the name of the
entity, that will hold audit information.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.audit_table_suffix</property>
</entry>
<entry>
_AUD
</entry>
<entry>
String that will be appended to the name of an audited entity to create the name of the
entity, that will hold audit information. If you audit an entity with a table name Person,
in the default setting Envers will generate a <literal>Person_AUD</literal> table to store
historical data.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.revision_field_name</property>
</entry>
<entry>
REV
</entry>
<entry>
Name of a field in the audit entity that will hold the revision number.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.revision_type_field_name</property>
</entry>
<entry>
REVTYPE
</entry>
<entry>
Name of a field in the audit entity that will hold the type of the revision (currently,
this can be: add, mod, del).
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.revision_on_collection_change</property>
</entry>
<entry>
true
</entry>
<entry>
Should a revision be generated when a not-owned relation field changes (this can be either
a collection in a one-to-many relation, or the field using "mappedBy" attribute in a
one-to-one relation).
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.do_not_audit_optimistic_locking_field</property>
</entry>
<entry>
true
</entry>
<entry>
When true, properties to be used for optimistic locking, annotated with
<literal>@Version</literal>, will be automatically not audited (their history won't be
stored; it normally doesn't make sense to store it).
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.store_data_at_delete</property>
</entry>
<entry>
false
</entry>
<entry>
Should the entity data be stored in the revision when the entity is deleted (instead of only
storing the id and all other properties as null). This is not normally needed, as the data is
present in the last-but-one revision. Sometimes, however, it is easier and more efficient to
access it in the last revision (then the data that the entity contained before deletion is
stored twice).
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.default_schema</property>
</entry>
<entry>
null (same schema as table being audited)
</entry>
<entry>
The default schema name that should be used for audit tables. Can be overridden using the
<literal>@AuditTable(schema="...")</literal> annotation. If not present, the schema will
be the same as the schema of the table being audited.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.default_catalog</property>
</entry>
<entry>
null (same catalog as table being audited)
</entry>
<entry>
The default catalog name that should be used for audit tables. Can be overridden using the
<literal>@AuditTable(catalog="...")</literal> annotation. If not present, the catalog will
be the same as the catalog of the normal tables.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.audit_strategy</property>
</entry>
<entry>
org.hibernate.envers.strategy.DefaultAuditStrategy
</entry>
<entry>
The audit strategy that should be used when persisting audit data. The default stores only
the revision, at which an entity was modified. An alternative, the
<literal>org.hibernate.envers.strategy.ValidityAuditStrategy</literal> stores both the
start revision and the end revision. Together these define when an audit row was valid,
hence the name ValidityAuditStrategy.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.audit_strategy_validity_end_rev_field_name</property>
</entry>
<entry>
REVEND
</entry>
<entry>
The column name that will hold the end revision number in audit entities. This property is
only valid if the validity audit strategy is used.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.audit_strategy_validity_store_revend_timestamp</property>
</entry>
<entry>
false
</entry>
<entry>
Should the timestamp of the end revision be stored, until which the data was valid, in
addition to the end revision itself. This is useful to be able to purge old Audit records
out of a relational database by using table partitioning. Partitioning requires a column
that exists within the table. This property is only evaluated if the ValidityAuditStrategy
is used.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name</property>
</entry>
<entry>
REVEND_TSTMP
</entry>
<entry>
Column name of the timestamp of the end revision until which the data was valid. Only used
if the ValidityAuditStrategy is used, and
<property>org.hibernate.envers.audit_strategy_validity_store_revend_timestamp</property>
evaluates to true
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.use_revision_entity_with_native_id</property>
</entry>
<entry>
true
</entry>
<entry>
Boolean flag that determines the strategy of revision number generation. Default
implementation of revision entity uses native identifier generator. If current database
engine does not support identity columns, users are advised to set this property to false.
In this case revision numbers are created by preconfigured
<classname>org.hibernate.id.enhanced.SequenceStyleGenerator</classname>. See:
<orderedlist>
<listitem><classname>org.hibernate.envers.DefaultRevisionEntity</classname></listitem>
<listitem><classname>org.hibernate.envers.enhanced.SequenceIdRevisionEntity</classname></listitem>
</orderedlist>
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.track_entities_changed_in_revision</property>
</entry>
<entry>
false
</entry>
<entry>
Should entity types, that have been modified during each revision, be tracked. The default
implementation creates <literal>REVCHANGES</literal> table that stores entity names
of modified persistent objects. Single record encapsulates the revision identifier
(foreign key to <literal>REVINFO</literal> table) and a string value. For more
information refer to <xref linkend="envers-tracking-modified-entities-revchanges"/>
and <xref linkend="envers-tracking-modified-entities-queries"/>.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.global_with_modified_flag</property>
</entry>
<entry>
false, can be individually overriden with <literal>@Audited(withModifiedFlag=true)</literal>
</entry>
<entry>
Should property modification flags be stored for all audited entities and all properties.
When set to true, for all properties an additional boolean column in the audit tables will
be created, filled with information if the given property changed in the given revision.
When set to false, such column can be added to selected entities or properties using the
<literal>@Audited</literal> annotation.
For more information refer to <xref linkend="envers-tracking-properties-changes"/>
and <xref linkend="envers-envers-tracking-properties-changes-queries"/>.
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.modified_flag_suffix</property>
</entry>
<entry>
_MOD
</entry>
<entry>
The suffix for columns storing "Modified Flags".
For example: a property called "age", will by default get modified flag with column name "age_MOD".
</entry>
</row>
<row>
<entry>
<property>org.hibernate.envers.embeddable_set_ordinal_field_name</property>
</entry>
<entry>
SETORDINAL
</entry>
<entry>
Name of column used for storing ordinal of the change in sets of embeddable elements.
</entry>
</row>
</tbody>
</tgroup>
</table>
<important>
<para>
The following configuration options have been added recently and should be regarded as experimental:
<orderedlist>
<listitem>
org.hibernate.envers.track_entities_changed_in_revision
</listitem>
<listitem>
org.hibernate.envers.using_modified_flag
</listitem>
<listitem>
org.hibernate.envers.modified_flag_suffix
</listitem>
</orderedlist>
</para>
</important>
</section>
<section>
<title>Additional mapping annotations</title>
<para>
The name of the audit table can be set on a per-entity basis, using the
<literal>@AuditTable</literal> annotation. It may be tedious to add this
annotation to every audited entity, so if possible, it's better to use a prefix/suffix.
</para>
<para>
If you have a mapping with secondary tables, audit tables for them will be generated in
the same way (by adding the prefix and suffix). If you wish to overwrite this behaviour,
you can use the <literal>@SecondaryAuditTable</literal> and
<literal>@SecondaryAuditTables</literal> annotations.
</para>
<para>
If you'd like to override auditing behaviour of some fields/properties inherited from
<interfacename>@Mappedsuperclass</interfacename> or in an embedded component, you can
apply the <literal>@AuditOverride(s)</literal> annotation on the subtype or usage site
of the component.
</para>
<para>
If you want to audit a relation mapped with <literal>@OneToMany+@JoinColumn</literal>,
please see <xref linkend="envers-mappingexceptions"/> for a description of the additional
<literal>@AuditJoinTable</literal> annotation that you'll probably want to use.
</para>
<para>
If you want to audit a relation, where the target entity is not audited (that is the case for example with
dictionary-like entities, which don't change and don't have to be audited), just annotate it with
<literal>@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)</literal>. Then, when reading historic
versions of your entity, the relation will always point to the "current" related entity.
</para>
<para>
If you'd like to audit properties of a superclass of an entity, which are not explicitly audited (which
don't have the <literal>@Audited</literal> annotation on any properties or on the class), you can list the
superclasses in the <literal>auditParents</literal> attribute of the <interfacename>@Audited</interfacename>
annotation. Please note that <literal>auditParents</literal> feature has been deprecated. Use
<literal>@AuditOverride(forClass = SomeEntity.class, isAudited = true/false)</literal> instead.
</para>
</section>
<section>
<title>Choosing an audit strategy</title>
<para>
After the basic configuration it is important to choose the audit strategy that will be used to persist
and retrieve audit information. There is a trade-off between the performance of persisting and the
performance of querying the audit information. Currently there two audit strategies.
</para>
<orderedlist>
<listitem>
<para>
The default audit strategy persists the audit data together with a start revision. For each row
inserted, updated or deleted in an audited table, one or more rows are inserted in the audit
tables, together with the start revision of its validity. Rows in the audit tables are never
updated after insertion. Queries of audit information use subqueries to select the applicable
rows in the audit tables. These subqueries are notoriously slow and difficult to index.
</para>
</listitem>
<listitem>
<para>
The alternative is a validity audit strategy. This strategy stores the start-revision and the
end-revision of audit information. For each row inserted, updated or deleted in an audited table,
one or more rows are inserted in the audit tables, together with the start revision of its
validity. But at the same time the end-revision field of the previous audit rows (if available)
are set to this revision. Queries on the audit information can then use 'between start and end
revision' instead of subqueries as used by the default audit strategy.
</para>
<para>
The consequence of this strategy is that persisting audit information will be a bit slower,
because of the extra updates involved, but retrieving audit information will be a lot faster.
This can be improved by adding extra indexes.
</para>
</listitem>
</orderedlist>
</section>
<section xml:id="envers-revisionlog">
<title>Revision Log</title>
<subtitle>Logging data for revisions</subtitle>
<para>
When Envers starts a new revision, it creates a new <firstterm>revision entity</firstterm> which stores
information about the revision. By default, that includes just
</para>
<orderedlist>
<listitem>
<para>
<firstterm>revision number</firstterm> - An integral value (<literal>int/Integer</literal> or
<literal>long/Long</literal>). Essentially the primary key of the revision
</para>
</listitem>
<listitem>
<para>
<firstterm>revision timestamp</firstterm> - either a <literal>long/Long</literal> or
<classname>java.util.Date</classname> value representing the instant at which the revision was made.
When using a <classname>java.util.Date</classname>, instead of a <literal>long/Long</literal> for
the revision timestamp, take care not to store it to a column data type which will loose precision.
</para>
</listitem>
</orderedlist>
<para>
Envers handles this information as an entity. By default it uses its own internal class to act as the
entity, mapped to the <literal>REVINFO</literal> table.
You can, however, supply your own approach to collecting this information which might be useful to
capture additional details such as who made a change or the ip address from which the request came. There
are 2 things you need to make this work.
</para>
<orderedlist>
<listitem>
<para>
First, you will need to tell Envers about the entity you wish to use. Your entity must use the
<interfacename>@org.hibernate.envers.RevisionEntity</interfacename> annotation. It must
define the 2 attributes described above annotated with
<interfacename>@org.hibernate.envers.RevisionNumber</interfacename> and
<interfacename>@org.hibernate.envers.RevisionTimestamp</interfacename>, respectively. You can extend
from <classname>org.hibernate.envers.DefaultRevisionEntity</classname>, if you wish, to inherit all
these required behaviors.
</para>
<para>
Simply add the custom revision entity as you do your normal entities. Envers will "find it". Note
that it is an error for there to be multiple entities marked as
<interfacename>@org.hibernate.envers.RevisionEntity</interfacename>
</para>
</listitem>
<listitem>
<para>
Second, you need to tell Envers how to create instances of your revision entity which is handled
by the <methodname>newRevision</methodname> method of the
<interfacename>org.jboss.envers.RevisionListener</interfacename> interface.
</para>
<para>
You tell Envers your custom <interfacename>org.hibernate.envers.RevisionListener</interfacename>
implementation to use by specifying it on the
<interfacename>@org.hibernate.envers.RevisionEntity</interfacename> annotation, using the
<methodname>value</methodname> attribute. If your <interfacename>RevisionListener</interfacename>
class is inaccessible from <interfacename>@RevisionEntity</interfacename> (e.g. exists in a different
module), set <property>org.hibernate.envers.revision_listener</property> property to it's fully
qualified name. Class name defined by the configuration parameter overrides revision entity's
<methodname>value</methodname> attribute.
</para>
</listitem>
</orderedlist>
<programlisting><![CDATA[@Entity
@RevisionEntity( MyCustomRevisionListener.class )
public class MyCustomRevisionEntity {
...
}
public class MyCustomRevisionListener implements RevisionListener {
public void newRevision(Object revisionEntity) {
( (MyCustomRevisionEntity) revisionEntity )...;
}
}
]]></programlisting>
<para>
An alternative method to using the <interfacename>org.hibernate.envers.RevisionListener</interfacename>
is to instead call the <methodname>getCurrentRevision</methodname> method of the
<interfacename>org.hibernate.envers.AuditReader</interfacename> interface to obtain the current revision,
and fill it with desired information. The method accepts a <literal>persist</literal> parameter indicating
whether the revision entity should be persisted prior to returning from this method. <literal>true</literal>
ensures that the returned entity has access to its identifier value (revision number), but the revision
entity will be persisted regardless of whether there are any audited entities changed. <literal>false</literal>
means that the revision number will be <literal>null</literal>, but the revision entity will be persisted
only if some audited entities have changed.
</para>
<example>
<title>Example of storing username with revision</title>
<programlisting>
<filename>ExampleRevEntity.java</filename><![CDATA[
package org.hibernate.envers.example;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.DefaultRevisionEntity;
import javax.persistence.Entity;
@Entity
@RevisionEntity(ExampleListener.class)
public class ExampleRevEntity extends DefaultRevisionEntity {
private String username;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
}]]></programlisting>
<programlisting>
<filename>ExampleListener.java</filename><![CDATA[
package org.hibernate.envers.example;
import org.hibernate.envers.RevisionListener;
import org.jboss.seam.security.Identity;
import org.jboss.seam.Component;
public class ExampleListener implements RevisionListener {
public void newRevision(Object revisionEntity) {
ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity;
Identity identity =
(Identity) Component.getInstance("org.jboss.seam.security.identity");
exampleRevEntity.setUsername(identity.getUsername());
}
}]]></programlisting>
</example>
<section xml:id="envers-tracking-modified-entities-revchanges">
<title>Tracking entity names modified during revisions</title>
<para>
By default entity types that have been changed in each revision are not being tracked. This implies the
necessity to query all tables storing audited data in order to retrieve changes made during
specified revision. Envers provides a simple mechanism that creates <literal>REVCHANGES</literal>
table which stores entity names of modified persistent objects. Single record encapsulates the revision
identifier (foreign key to <literal>REVINFO</literal> table) and a string value.
</para>
<para>
Tracking of modified entity names can be enabled in three different ways:
</para>
<orderedlist>
<listitem>
<para>
Set <property>org.hibernate.envers.track_entities_changed_in_revision</property> parameter to
<literal>true</literal>. In this case
<classname>org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity</classname> will
be implicitly used as the revision log entity.
</para>
</listitem>
<listitem>
<para>
Create a custom revision entity that extends
<classname>org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity</classname> class.
</para>
<programlisting>
<![CDATA[@Entity
@RevisionEntity
public class ExtendedRevisionEntity
extends DefaultTrackingModifiedEntitiesRevisionEntity {
...
}]]></programlisting>
</listitem>
<listitem>
<para>
Mark an appropriate field of a custom revision entity with
<interfacename>@org.hibernate.envers.ModifiedEntityNames</interfacename> annotation. The property is
required to be of <literal><![CDATA[Set<String>]]></literal> type.
</para>
<programlisting>
<![CDATA[@Entity
@RevisionEntity
public class AnnotatedTrackingRevisionEntity {
...
@ElementCollection
@JoinTable(name = "REVCHANGES", joinColumns = @JoinColumn(name = "REV"))
@Column(name = "ENTITYNAME")
@ModifiedEntityNames
private Set<String> modifiedEntityNames;
...
}]]></programlisting>
</listitem>
</orderedlist>
<para>
Users, that have chosen one of the approaches listed above, can retrieve all entities modified in a
specified revision by utilizing API described in <xref linkend="envers-tracking-modified-entities-queries"/>.
</para>
<para>
Users are also allowed to implement custom mechanism of tracking modified entity types. In this case, they
shall pass their own implementation of
<interfacename>org.hibernate.envers.EntityTrackingRevisionListener</interfacename> interface as the value
of <interfacename>@org.hibernate.envers.RevisionEntity</interfacename> annotation.
<interfacename>EntityTrackingRevisionListener</interfacename> interface exposes one method that notifies
whenever audited entity instance has been added, modified or removed within current revision boundaries.
</para>
<example>
<title>Custom implementation of tracking entity classes modified during revisions</title>
<programlisting>
<filename>CustomEntityTrackingRevisionListener.java</filename>
<![CDATA[
public class CustomEntityTrackingRevisionListener
implements EntityTrackingRevisionListener {
@Override
public void entityChanged(Class entityClass, String entityName,
Serializable entityId, RevisionType revisionType,
Object revisionEntity) {
String type = entityClass.getName();
((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityType(type);
}
@Override
public void newRevision(Object revisionEntity) {
}
}]]></programlisting>
<programlisting>
<filename>CustomTrackingRevisionEntity.java</filename>
<![CDATA[
@Entity
@RevisionEntity(CustomEntityTrackingRevisionListener.class)
public class CustomTrackingRevisionEntity {
@Id
@GeneratedValue
@RevisionNumber
private int customId;
@RevisionTimestamp
private long customTimestamp;
@OneToMany(mappedBy="revision", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
private Set<ModifiedEntityTypeEntity> modifiedEntityTypes =
new HashSet<ModifiedEntityTypeEntity>();
public void addModifiedEntityType(String entityClassName) {
modifiedEntityTypes.add(new ModifiedEntityTypeEntity(this, entityClassName));
}
...
}
]]></programlisting>
<programlisting>
<filename>ModifiedEntityTypeEntity.java</filename>
<![CDATA[
@Entity
public class ModifiedEntityTypeEntity {
@Id
@GeneratedValue
private Integer id;
@ManyToOne
private CustomTrackingRevisionEntity revision;
private String entityClassName;
...
}
]]></programlisting>
<programlisting><![CDATA[CustomTrackingRevisionEntity revEntity =
getAuditReader().findRevision(CustomTrackingRevisionEntity.class, revisionNumber);
Set<ModifiedEntityTypeEntity> modifiedEntityTypes = revEntity.getModifiedEntityTypes()]]></programlisting>
</example>
</section>
</section>
<section xml:id="envers-tracking-properties-changes">
<title>Tracking entity changes at property level</title>
<para>
By default the only information stored by Envers are revisions of modified entities.
This approach lets user create audit queries based on historical values of entity's properties.
Sometimes it is useful to store additional metadata for each revision, when you are interested also in
the type of changes, not only about the resulting values. The feature described in
<xref linkend="envers-tracking-modified-entities-revchanges"/>
makes it possible to tell which entities were modified in given revision.
Feature described here takes it one step further. "Modification Flags" enable Envers to track which
properties of audited entities were modified in a given revision.
</para>
<para>
Tracking entity changes at property level can be enabled by:
</para>
<orderedlist>
<listitem>
<para>
setting <property>org.hibernate.envers.global_with_modified_flag</property> configuration
property to <literal>true</literal>. This global switch will cause adding modification flags
for all audited properties in all audited entities.
</para>
</listitem>
<listitem>
<para>
using <literal>@Audited(withModifiedFlag=true)</literal> on a property or on an entity.
</para>
</listitem>
</orderedlist>
<para>
The trade-off coming with this functionality is an increased size of
audit tables and a very little, almost negligible, performance drop
during audit writes. This is due to the fact that every tracked
property has to have an accompanying boolean column in the
schema that stores information about the property's modifications. Of
course it is Envers' job to fill these columns accordingly - no additional work by the
developer is required. Because of costs mentioned, it is recommended
to enable the feature selectively, when needed with use of the
granular configuration means described above.
</para>
<para>
To see how "Modified Flags" can be utilized, check out the very
simple query API that uses them: <xref linkend="envers-envers-tracking-properties-changes-queries"/>.
</para>
</section>
<section xml:id="envers-queries">
<title>Queries</title>
<para>
You can think of historic data as having two dimension. The first - horizontal -
is the state of the database at a given revision. Thus, you can
query for entities as they were at revision N. The second - vertical - are the
revisions, at which entities changed. Hence, you can query for revisions,
in which a given entity changed.
</para>
<para>
The queries in Envers are similar to Hibernate Criteria queries, so if you are common with them,
using Envers queries will be much easier.
</para>
<para>
The main limitation of the current queries implementation is that you cannot
traverse relations. You can only specify constraints on the ids of the
related entities, and only on the "owning" side of the relation. This however
will be changed in future releases.
</para>
<para>
Please note, that queries on the audited data will be in many cases much slower
than corresponding queries on "live" data, as they involve correlated subselects.
</para>
<para>
In the future, queries will be improved both in terms of speed and possibilities, when using the valid-time
audit strategy, that is when storing both start and end revisions for entities. See
<xref linkend="configuration"/>.
</para>
<section xml:id="entities-at-revision">
<title>Querying for entities of a class at a given revision</title>
<para>
The entry point for this type of queries is:
</para>
<programlisting><![CDATA[AuditQuery query = getAuditReader()
.createQuery()
.forEntitiesAtRevision(MyEntity.class, revisionNumber);]]></programlisting>
<para>
You can then specify constraints, which should be met by the entities returned, by
adding restrictions, which can be obtained using the <literal>AuditEntity</literal>
factory class. For example, to select only entities, where the "name" property
is equal to "John":
</para>
<programlisting><![CDATA[query.add(AuditEntity.property("name").eq("John"));]]></programlisting>
<para>
And to select only entites that are related to a given entity:
</para>
<programlisting><![CDATA[query.add(AuditEntity.property("address").eq(relatedEntityInstance));
// or
query.add(AuditEntity.relatedId("address").eq(relatedEntityId));]]></programlisting>
<para>
You can limit the number of results, order them, and set aggregations and projections
(except grouping) in the usual way.
When your query is complete, you can obtain the results by calling the
<literal>getSingleResult()</literal> or <literal>getResultList()</literal> methods.
</para>
<para>
A full query, can look for example like this:
</para>
<programlisting><![CDATA[List personsAtAddress = getAuditReader().createQuery()
.forEntitiesAtRevision(Person.class, 12)
.addOrder(AuditEntity.property("surname").desc())
.add(AuditEntity.relatedId("address").eq(addressId))
.setFirstResult(4)
.setMaxResults(2)
.getResultList();]]></programlisting>
</section>
<section xml:id="revisions-of-entity">
<title>Querying for revisions, at which entities of a given class changed</title>
<para>
The entry point for this type of queries is:
</para>
<programlisting><![CDATA[AuditQuery query = getAuditReader().createQuery()
.forRevisionsOfEntity(MyEntity.class, false, true);]]></programlisting>
<para>
You can add constraints to this query in the same way as to the previous one.
There are some additional possibilities:
</para>
<orderedlist>
<listitem>
<para>
using <literal>AuditEntity.revisionNumber()</literal> you can specify constraints, projections
and order on the revision number, in which the audited entity was modified
</para>
</listitem>
<listitem>
<para>
similarly, using <literal>AuditEntity.revisionProperty(propertyName)</literal> you can specify constraints,
projections and order on a property of the revision entity, corresponding to the revision
in which the audited entity was modified
</para>
</listitem>
<listitem>
<para>
<literal>AuditEntity.revisionType()</literal> gives you access as above to the type of
the revision (ADD, MOD, DEL).
</para>
</listitem>
</orderedlist>
<para>
Using these methods,
you can order the query results by revision number, set projection or constraint
the revision number to be greater or less than a specified value, etc. For example, the
following query will select the smallest revision number, at which entity of class
<literal>MyEntity</literal> with id <literal>entityId</literal> has changed, after revision
number 42:
</para>
<programlisting><![CDATA[Number revision = (Number) getAuditReader().createQuery()
.forRevisionsOfEntity(MyEntity.class, false, true)
.setProjection(AuditEntity.revisionNumber().min())
.add(AuditEntity.id().eq(entityId))
.add(AuditEntity.revisionNumber().gt(42))
.getSingleResult();]]></programlisting>
<para>
The second additional feature you can use in queries for revisions is the ability
to maximalize/minimize a property. For example, if you want to select the
revision, at which the value of the <literal>actualDate</literal> for a given entity
was larger then a given value, but as small as possible:
</para>
<programlisting><![CDATA[Number revision = (Number) getAuditReader().createQuery()
.forRevisionsOfEntity(MyEntity.class, false, true)
// We are only interested in the first revision
.setProjection(AuditEntity.revisionNumber().min())
.add(AuditEntity.property("actualDate").minimize()
.add(AuditEntity.property("actualDate").ge(givenDate))
.add(AuditEntity.id().eq(givenEntityId)))
.getSingleResult();
]]></programlisting>
<para>
The <literal>minimize()</literal> and <literal>maximize()</literal> methods return a criteria,
to which you can add constraints, which must be met by the entities with the
maximized/minimized properties.
</para>
<para>
You probably also noticed that there are two boolean parameters, passed when
creating the query. The first one, <literal>selectEntitiesOnly</literal>, is only valid when
you don't set an explicit projection. If true, the result of the query will be
a list of entities (which changed at revisions satisfying the specified
constraints).
</para>
<para>
If false, the result will be a list of three element arrays. The
first element will be the changed entity instance. The second will be an entity
containing revision data (if no custom entity is used, this will be an instance
of <literal>DefaultRevisionEntity</literal>). The third will be the type of the
revision (one of the values of the <literal>RevisionType</literal> enumeration:
ADD, MOD, DEL).
</para>
<para>
The second parameter, <literal>selectDeletedEntities</literal>, specifies if revisions,
in which the entity was deleted should be included in the results. If yes, such entities
will have the revision type DEL and all fields, except the id,
<literal>null</literal>.
</para>
</section>
<section xml:id="envers-envers-tracking-properties-changes-queries">
<title>Querying for revisions of entity that modified given property</title>
<para>
For the two types of queries described above it's possible to use
special Audit criteria called
<literal>hasChanged()</literal>
and
<literal>hasNotChanged()</literal>
that makes use of the functionality
described in <xref linkend="envers-tracking-properties-changes"/>.
They're best suited for vertical queries,
however existing API doesn't restrict their usage for horizontal
ones.
Let's have a look at following examples:
</para>
<programlisting><![CDATA[
AuditQuery query = getAuditReader().createQuery()
.forRevisionsOfEntity(MyEntity.class, false, true)
.add(AuditEntity.id().eq(id));
.add(AuditEntity.property("actualDate").hasChanged())]]>
</programlisting>
<para>
This query will return all revisions of MyEntity with given id,
where the
<property>actualDate</property>
property has been changed.
Using this query we won't get all other revisions in which
<property>actualDate</property>
wasn't touched. Of course nothing prevents user from combining
hasChanged condition with some additional criteria - add method
can be used here in a normal way.
</para>
<programlisting><![CDATA[
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision(MyEntity.class, revisionNumber)
.add(AuditEntity.property("prop1").hasChanged())
.add(AuditEntity.property("prop2").hasNotChanged());]]>
</programlisting>
<para>
This query will return horizontal slice for MyEntity at the time
revisionNumber was generated. It will be limited to revisions
that modified
<property>prop1</property>
but not <property>prop2</property>.
Note that the result set will usually also contain revisions
with numbers lower than the revisionNumber, so we cannot read
this query as "Give me all MyEntities changed in revisionNumber
with
<property>prop1</property>
modified and
<property>prop2</property>
untouched". To get such result we have to use the
<literal>forEntitiesModifiedAtRevision</literal> query: