(no commit message)
[cdmlib.git] / src / docbkx / versioning.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <chapter version="5.0" xml:id="versioning"
3 xmlns="http://docbook.org/ns/docbook"
4 xmlns:xlink="http://www.w3.org/1999/xlink"
5 xmlns:ns5="http://www.w3.org/1999/xhtml"
6 xmlns:ns4="http://www.w3.org/2000/svg"
7 xmlns:ns3="http://www.w3.org/1998/Math/MathML"
8 xmlns:ns="http://docbook.org/ns/docbook">
9 <info>
10 <title>Versioning</title>
11 </info>
12
13 <section>
14 <para>A significant use-case that the CDM aims to support is that of
15 web-based or networked nomenclators, taxonomic treatments, and other
16 applications that serve authoritative, dynamic data for (re-)use by
17 taxonomists and other software applications. As an example, a CDM store
18 containing a web-based monograph or revision of a particular plant or
19 animal family might be referenced by other taxonomists, or other taxonomic
20 databases that deal with the same taxa. To allow applications to record
21 and resolve changes to data over time, for example, to allow users or
22 client applications to determine how a taxonomic classification or species
23 page has been altered since they last accessed that information, the CDM
24 has a fine-grained versioning functionality that records changes to
25 objects and their relationships, and allows the prior state of the dataset
26 to be reconstructed.</para>
27
28 <para>The CDM uses <link
29 xlink:href="http://www.jboss.org/envers/">hibernate-envers</link>, a
30 versioning / auditing library that is part of the hibernate core library.
31 The versioning functionality is limited by the features that envers
32 provides. Envers stores changes to entities on a per-transaction basis.
33 Consequently, it is not possible to resolve changes that take place within
34 the same transaction. Each transaction results in the creation of an
35 <classname>AuditEvent</classname> object that provides metadata about the
36 audit event and also allows the state of the database at that point to be
37 reconstructed (because an <classname>AuditEvent</classname> represents a
38 point in time across the entire database, rather than on a per-object
39 basis). To learn more about envers and the way that it versions data,
40 check out the presentation given by its creator, Adam Warski <link
41 xlink:href="http://jboss.org/file-access/default/members/envers/downloads/presentations/envers_devoxx.pdf">here</link>.</para>
42
43 <para>Versioning is enabled by default, and calls to methods like
44 <methodname>save</methodname>, <methodname>update</methodname>, and
45 <methodname>delete</methodname>, will automatically result in data being
46 versioned. Application developers only need to be aware of the existence
47 of versioning when reading data, and only then if they wish to retrieve an
48 object in its prior state. If applications wish to retrieve objects from
49 the current state of the database, they do not need to perform any
50 additional operations. <methodname /></para>
51
52 <para>Because versions of objects are related to a global
53 <classname>AuditEvent</classname>, and because applications may call
54 several service layer methods when retrieving data for presentation in a
55 particular view, the CDM stores the <classname>AuditEvent</classname> in
56 the static field of an object called
57 <classname>AuditEventContextHolder</classname>, allowing the CDM and any
58 application code to discover which particular
59 <classname>AuditEvent</classname> a view relates to without needing to
60 pass the <classname>AuditEvent</classname> explicitly as a method
61 parameter (this pattern is borrows from the
62 <classname>SecurityContext</classname> class in Spring-Security).</para>
63
64 <para>To query the CDM at a particular <classname>AuditEvent</classname>,
65 applications need to place the <classname>AuditEvent</classname> in to the
66 <classname>AuditEventContextHolder</classname> and then call DAO methods
67 as usual.</para>
68
69 <programlisting>// This would retrieve the current version of the taxon with a matching uuid.
70 Taxon taxon = taxonDao.find(uuid);
71
72 // Set the audit event you're interested in
73 AuditEventContextHolder.<emphasis>getContext</emphasis>().setAuditEvent(auditEvent);
74
75 // This method call now retrieves the taxon with a matching uuid at the audit event in context
76 // or null if the taxon did not exist at that point.
77 Taxon taxon = taxonDao.find(uuid);
78
79 // Now clear the context
80 AuditEventContextHolder.<emphasis>clearContext</emphasis>();
81
82 // Further calls to the persistence layer will return the most recent objects
83 </programlisting>
84
85 <para>Not all DAO methods are available in non-current contexts, either
86 because they require certain methods that Envers doesn't currently support
87 (such as case-insensitive string comparison), or are across relationships
88 - currently envers does not support queries that place restrictions on
89 related entities. In some cases this will be addressed in future releases
90 of envers, and the CDM will incorporate these new releases as they occur.
91 Some methods rely on the free-text-search functionality provided by
92 hibernate search. Because hibernate search (and apache Lucene) are based
93 on an optimized set of index files that reflect the current state of the
94 database, it is not possible to search these indices at prior events. It
95 is unlikely that the free-text-search functionality will ever be available
96 in non-current contexts. If an application calls such a method in a
97 non-current context, an
98 <exceptionname>OperationNotSupportedInPriorViewException</exceptionname>
99 is thrown, giving applications an operation to recover.</para>
100
101 <para>Objects retrieved in prior contexts can be initialized using the
102 <parameter>propertyPaths</parameter> parameter, or (if the transaction is
103 still open) by calling accessor methods in domain objects directly (just
104 as you would with normal hibernate-managed entities).</para>
105
106 <para>In addition to being able to retrieve objects at a given state, the
107 DAOs implement the <interfacename>IVersionableDao</interfacename>
108 interface that offers five specific methods for working with versioned
109 objects.</para>
110
111 <table frame="all" xml:id="iversionabledao-methods">
112 <title><interfacename>IVersionableDao</interfacename> methods</title>
113
114 <tgroup align="left" cols="2" colsep="1" rowsep="1">
115 <colspec colname="c1" />
116
117 <colspec colname="c2" />
118
119 <thead>
120 <row>
121 <entry>Method</entry>
122
123 <entry>Description</entry>
124 </row>
125 </thead>
126
127 <tbody>
128 <row>
129 <entry>
130 <methodsynopsis>
131 <type>List&lt;AuditEventRecord&lt;T&gt;&gt;</type>
132
133 <methodname>getAuditEvents</methodname>
134
135 <methodparam>
136 <type>T</type>
137
138 <parameter>t</parameter>
139 </methodparam>
140
141 <methodparam>
142 <type>Integer</type>
143
144 <parameter>limit</parameter>
145 </methodparam>
146
147 <methodparam>
148 <type>Integer</type>
149
150 <parameter>start</parameter>
151 </methodparam>
152
153 <methodparam>
154 <type>AuditEventSort</type>
155
156 <parameter>sort</parameter>
157 </methodparam>
158
159 <methodparam>
160 <type>List&lt;String&gt;</type>
161
162 <parameter>propertyPaths</parameter>
163 </methodparam>
164 </methodsynopsis>
165 </entry>
166
167 <entry>
168 <para>Returns a list of audit events (in order) which affected
169 the state of an entity t. The events returned either start at
170 the <classname>AuditEvent</classname> in context and go forward
171 in time (<parameter>AuditEventSort.FORWARDS</parameter>) or
172 backwards in time
173 (<parameter>AuditEventSort.BACKWARDS</parameter>). If the
174 <classname>AuditEventContext</classname> is set to null, or to
175 <parameter>AuditEvent.CURRENT_VIEW</parameter>, then all
176 relevant AuditEvents are returned.</para>
177 </entry>
178 </row>
179
180 <row>
181 <entry>
182 <methodsynopsis>
183 <type>int</type>
184
185 <methodname>countAuditEvents</methodname>
186
187 <methodparam>
188 <type>T</type>
189
190 <parameter>t</parameter>
191 </methodparam>
192
193 <methodparam>
194 <type>AuditEventSort</type>
195
196 <parameter>sort</parameter>
197 </methodparam>
198 </methodsynopsis>
199 </entry>
200
201 <entry>
202 <para>Returns a count of audit events which affected the state
203 of an entity t.</para>
204 </entry>
205 </row>
206
207 <row>
208 <entry>
209 <methodsynopsis>
210 <type>AuditEventRecord&lt;T&gt;</type>
211
212 <methodname>getNextAuditEvent</methodname>
213
214 <methodparam>
215 <type>T</type>
216
217 <parameter>t</parameter>
218 </methodparam>
219 </methodsynopsis>
220 </entry>
221
222 <entry>
223 <para>A convenience method which returns a record of the next
224 (relative to the audit event in context).</para>
225 </entry>
226 </row>
227
228 <row>
229 <entry>
230 <methodsynopsis>
231 <type>AuditEventRecord&lt;T&gt;</type>
232
233 <methodname>getPreviousAuditEvent</methodname>
234
235 <methodparam>
236 <type>T</type>
237
238 <parameter>t</parameter>
239 </methodparam>
240 </methodsynopsis>
241 </entry>
242
243 <entry>
244 <para>A convenience method which returns a record of the
245 previous (relative to the audit event in context).</para>
246 </entry>
247 </row>
248
249 <row>
250 <entry>
251 <methodsynopsis>
252 <type>boolean</type>
253
254 <methodname>existed</methodname>
255
256 <methodparam>
257 <type>UUID</type>
258
259 <parameter>uuid</parameter>
260 </methodparam>
261 </methodsynopsis>
262 </entry>
263
264 <entry>
265 <para>Returns true if an object with uuid matching the one
266 supplied either currently exists, or existed previously and has
267 been deleted from the current view.</para>
268 </entry>
269 </row>
270 </tbody>
271 </tgroup>
272 </table>
273 </section>
274 </chapter>