Project

General

Profile

Download (10.6 KB) Statistics
| Branch: | Tag: | Revision:
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>
(10-10/10)