1<!-- $Id: package.html,v 1.1 2008/02/07 17:12:27 mark Exp $ --> 2<html> 3<body> 4Utilities for managing class evolution of persistent objects. 5 6<h1>Class Evolution</h1> 7 8<p>For persistent data that is not short lived, changes to persistent classes 9are almost inevitable. Some changes are compatible with existing types, and 10data conversion for these changes is performed automatically and transparently. 11Other changes are not compatible with existing types. Mutations can be used to 12explicitly manage many types of incompatible changes.</p> 13 14<p>Not all incompatible class changes can be handled via mutations. For 15example, complex refactoring may require a transformation that manipulates 16multiple entity instances at once. Such changes are not possible with 17mutations but can be made by performing a <a href="#storeConversion">store 18conversion</a>.</p> 19 20<p>The different categories of type changes are described below.</p> 21 22<h2>Key Field Changes</h2> 23 24<p>Unlike entity data, key data is not versioned. Therefore, the physical key 25format for an index is fixed once the index has been opened, and the changes 26allowed for key fields are very limited. The only changes allowed for key 27fields are:</p> 28<ul> 29<li>The name of a key field may be changed, as long as this change is 30accompanied by a {@link com.sleepycat.persist.evolve.Renamer} mutation.</li> 31<li>A primitive type may be changed to its corresponding primitive wrapper 32type. This is a compatible change.</li> 33<li>For primary key fields and fields of a composite key class, a primitive 34wrapper type may be changed to its corresponding primitive type. This is 35allowed because these key fields with reference types may never have null 36values. This is a compatible change.</li> 37</ul> 38 39<p>Any other changes to a key field are incompatible and may be made only by 40performing a <a href="#storeConversion">store conversion</a>.</p> 41 42<p>Key ordering, including the behavior of a custom {@link 43java.lang.Comparable}, is also fixed, since keys are stored in order in the 44index. The specifications for key ordering may not be changed, and the 45developer is responsible for not changing the behavior of a {@code Comparable} 46key class. <strong>WARNING:</strong>: Changing the behavior of a {@code 47Comparable} key class is likely to make the index unusable.</p> 48 49<h2>Compatible Type Changes</h2> 50 51<p>Entity data, unlike key data, is versioned. Therefore, some changes can be 52made compatibly and other changes can be handled via mutations. Compatible 53changes are defined below. To make a compatible class change, a mutation is 54not required; however, the class version must be assigned a new (greater) 55integer value.</p> 56 57<p>Changes to a class hierarchy are compatible in some cases. A new class may 58be inserted in the hierarchy. A class may be deleted from the hierarchy as 59long as one of the following is true: 1) it contains no persistent fields, 2) 60any persistent fields are deleted with field Deleter mutations, or 3) the class 61is deleted with a class Deleter mutation. Classes in an existing hierarchy may 62not be reordered compatibly, and fields may not moved from one class to another 63compatibly; for such changes a class Converter mutation is required.</p> 64 65<p>Changes to field types in entity class definitions are compatible when they 66conform to the Java Language Specification definitions for <a 67href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.2">Widening 68Primitive Conversions</a> and <a 69href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.5">Widening 70Reference Conversions</a>. For example, a smaller integer 71type may be changed to a larger integer type, and a reference type may be 72changed to one of its supertypes. Automatic widening conversions are performed 73as described in the Java Language Specification.</p> 74 75<p>Primitive types may also be compatibly changed to their corresponding 76primitive wrapper types, or to the wrapper type for a widened primitive type. 77However, changing from a primitive wrapper type to a primitive type is not a 78compatible change since existing null values could not be represented.</p> 79 80<p>Integer primitive types (byte, short, char, int, long) and their primitive 81wrapper types may be compatibly changed to the BigInteger type.</p> 82 83<p>In addition, adding fields to a class is a compatible change. When a 84persistent instance of a class is read that does not contain the new field, the 85new field is initialized by the default constructor.</p> 86 87<p>All other changes to instance fields are considered incompatible. 88Incompatible changes may be handled via mutations, as described next.</p> 89 90<p>Note that whenever a class is changed, either compatibly or incompatibly, a 91new (higher) class version number must be assigned. See {@link 92com.sleepycat.persist.model.Entity#version} and {@link 93com.sleepycat.persist.model.Persistent#version} for information on assigning 94class version numbers.</p> 95 96<h2>Mutations</h2> 97 98<p>There are three types of mutations: {@link 99com.sleepycat.persist.evolve.Renamer}, {@link 100com.sleepycat.persist.evolve.Deleter} and {@link 101com.sleepycat.persist.evolve.Converter}.</p> 102 103<p>A class or field can be renamed using a {@link 104com.sleepycat.persist.evolve.Renamer}. Renaming is not expensive, since it 105does not involve conversion of instance data.</p> 106 107<p>A class or field can be deleted using a {@link 108com.sleepycat.persist.evolve.Deleter}.</p> 109<ul> 110<li>Deleting an entity class causes removal of the primary and secondary 111indices for the store, on other words, removal of all store entities for that 112class and its subclasses. Removal is performed when the store is opened. A 113{@link com.sleepycat.persist.evolve.Deleter} should be used for an entity class 114in all of the following circumstances: 115 <ul> 116 <li>When removing the entity class itself.</li> 117 <li>When removing {@link com.sleepycat.persist.model.Entity} from the class 118 to make it non-persistent.</li> 119 <li>When removing {@link com.sleepycat.persist.model.Entity} from the class 120 and adding {@link com.sleepycat.persist.model.Persistent}, to use it as an 121 embedded persistent class but not an entity class. The version of the class 122 must be incremented in this case.</li> 123 </ul> 124</li> 125 126<li>Deleting a non-entity class does not itself cause deletion of instance 127data, but is needed to inform DPL that the deleted class will not be used. 128Instances of the deleted class must be handled (discarded or converted to 129another class) by {@link com.sleepycat.persist.evolve.Deleter} or {@link 130com.sleepycat.persist.evolve.Converter} mutations for the field or enclosing 131class that contain embedded instances of the deleted class. A {@link 132com.sleepycat.persist.evolve.Deleter} should be used for a non-entity class in 133all of the following circumstances: 134 <ul> 135 <li>When removing the persistent class itself.</li> 136 <li>When removing {@link com.sleepycat.persist.model.Persistent} from the 137 class to make it non-persistent.</li> 138 <li>When removing {@link com.sleepycat.persist.model.Persistent} from the 139 class and adding {@link com.sleepycat.persist.model.Entity}, to use it as an 140 entity class but not an embedded persistent class. The version of the class 141 must be incremented in this case.</li> 142 </ul> 143</li> 144 145<li>Deleting a field causes automatic conversion of the instances containing 146that field, in order to discard the field values.</li> 147</ul> 148 149<p>Other incompatible changes are handled by creating a {@link 150com.sleepycat.persist.evolve.Converter} mutation and implementing a {@link 151com.sleepycat.persist.evolve.Conversion#convert Conversion.convert} method that 152manipulates the raw objects and/or simple values directly. The {@code convert} 153method is passed an object of the old incompatible type and it returns an 154object of a current type.</p> 155 156<p>Conversions can be specified in two ways: for specific fields or for all 157instances of a class. A different {@link 158com.sleepycat.persist.evolve.Converter} constructor is used in each case. 159Field-specific conversions are used instead of class conversions when both are 160applicable.</p> 161 162<p>Note that each mutation is applied to a specific class version number. The 163class version must be explicitly specified in a mutation for two reasons:</p> 164<ol> 165<li>This provides safety in the face of multiple unconverted versions of a 166given type. Without a version, a single conversion method would have to handle 167multiple input types, and would have to distinguish between them by examining 168the data or type information.</li> 169<li>This allows arbitrary changes to be made. For example, a series of name 170changes may reuse a given name for more than one version. To identify the 171specific type being converted or renamed, a version number is needed.</li> 172</ol> 173<p>See {@link com.sleepycat.persist.model.Entity#version} and {@link 174com.sleepycat.persist.model.Persistent#version} for information on assigning 175class version numbers.</p> 176 177<p>Mutations are therefore responsible for converting each existing 178incompatible class version to the current version as defined by a current class 179definition. For example, consider that class-version A-1 is initially changed 180to A-2 and a mutation is added for converting A-1 to A-2. If later changes in 181version A-3 occur before converting all A-1 instances to version A-2, the 182converter for A-1 will have to be changed. Instead of converting from A-1 to 183A-2 it will need to convert from A-1 to A-3. In addition, a mutation 184converting A-2 to A-3 will be needed.</p> 185 186<p>When a {@link com.sleepycat.persist.evolve.Converter} mutation applies to a 187given object, other mutations that may apply to that object are not 188automatically performed. It is the responsibility of the {@link 189com.sleepycat.persist.evolve.Converter} to return an object that conforms to 190the current class definition, including renaming fields and classes. If the 191input object has nested objects or superclasses that also need conversion, the 192converter must perform these nested conversions before returning the final 193converted object. This rule avoids the complexity and potential errors that 194could result if a converter mutation were automatically combined with other 195mutations in an arbitrary manner.</p> 196 197<p>The {@link com.sleepycat.persist.EntityStore#evolve EntityStore.evolve} 198method may optionally be used to ensure that all instances of an old class 199version are converted to the current version.</p> 200 201<h2>Other Metadata Changes</h2> 202 203<p>When a class that happens to be an entity class is renamed, it remains an 204entity class. When a field that happens to be a primary or 205secondary key field is renamed, its metadata remains intact as well.</p> 206 207<p>When the {@link com.sleepycat.persist.model.SecondaryKey} annotation is 208added to an <em>existing</em> field, a new index is created automatically. The 209new index will be populated by reading the entire primary index when the 210primary index is opened.</p> 211 212<p>When the {@link com.sleepycat.persist.model.SecondaryKey} annotation is 213included with a <em>new</em> field, a new index is created automatically. The 214new field is required to be a reference type (not a primitive) and must be 215initialized to null (the default behavior) in the default constructor. 216Entities will be indexed by the field when they are stored with a non-null key 217value.</p> 218 219<p>When a field with the {@link com.sleepycat.persist.model.SecondaryKey} 220annotation is deleted, or when the {@link 221com.sleepycat.persist.model.SecondaryKey} annotation is removed from a field 222without deleting it, the secondary index is removed (dropped). Removal occurs 223when the store is opened.</p> 224 225<p>The {@link com.sleepycat.persist.model.SecondaryKey#relate 226SecondaryKey.relate} property may NOT be changed. All other properties of a 227{@link com.sleepycat.persist.model.SecondaryKey} may be changed, although 228avoiding changes that cause foreign key integrity errors is the responsibility 229of the application developer. For example, if the {@link 230com.sleepycat.persist.model.SecondaryKey#relatedEntity} property is added but 231not all existing secondary keys reference existing primary keys for the related 232entity, foreign key integrity errors may occur.</p> 233 234<p>The {@link com.sleepycat.persist.model.PrimaryKey} annotation may NOT be 235removed from a field in an entity class.</p> 236 237<p>The {@link com.sleepycat.persist.model.PrimaryKey#sequence} property may be 238added, removed, or changed to a different name.</p> 239 240<p>The {@link com.sleepycat.persist.model.Persistent#proxyFor} property may be 241NOT be added, removed, or changed to a different class.</p> 242 243<h2>Warnings on Testing and Backups</h2> 244 245<p>The application developer is responsible for verifying that class evolution 246works properly before deploying with a changed set of persistent classes. The 247DPL will report errors when old class definitions cannot be evolved, for 248example, when a mutation is missing. To test that no such errors will occur, 249application test cases must include instances of all persistent classes.</p> 250 251<p>Converter mutations require special testing. Since the application 252conversion method is allowed to return instances of any type, the DPL cannot 253check that the proper type is returned until the data is accessed. To avoid 254data access errors, application test cases must cover converter mutations for 255all potential input and output types.</p> 256 257<p>When secondary keys are dropped or entity classes are deleted, the 258underlying databases are deleted and cannot be recovered from the store. This 259takes place when the store is opened. It is strongly recommended that a backup 260of the entire store is made before opening the store and causing class 261evolution to proceed.</p> 262 263<h2><a name="storeConversion">Store Conversion<a/></h2> 264 265<p>When mutations are not sufficient for handling class changes, a full store 266conversion may be performed. This is necessary for two particular types of 267class changes:</p> 268<ul> 269<li>A change to a physical key format, for example, a change from type 270{@code int} to type {@code long}.</li> 271<li>A conversion that involves multiple entities at once, for example, 272combining two separate entity classes into a new single entity class.</li> 273</ul> 274 275<p>To perform a full store conversion, a program is written that performs the 276following steps to copy the data from the old store to a new converted 277store:</p> 278<ol> 279<li>The old store is opened as a {@link com.sleepycat.persist.raw.RawStore} and 280the new store is opened as an {@link com.sleepycat.persist.EntityStore}.</li> 281<li>All entities are read from the old store. Entities are read using a {@link 282com.sleepycat.persist.raw.RawStore} to allow access to entities for which no 283compatible class exists.</li> 284<li>The {@link com.sleepycat.persist.raw.RawObject} entities are then converted 285to the format desired. Raw objects can be arbitrarily manipulated as needed. 286The updated raw objects must conform to the new evolved class definitions.</li> 287<li>The updated raw entities are converted to live objects by calling the 288{@link com.sleepycat.persist.model.EntityModel#convertRawObject 289EntityModel.convertRawObject} method of the new store. This method converts 290raw objects obtained from a different store, as long as they conform to the new 291evolved class definitions.</li> 292<li>The new live objects are written to the new {@link 293com.sleepycat.persist.EntityStore} using a {@link 294com.sleepycat.persist.PrimaryIndex} as usual.</li> 295</ol> 296 297<p>To perform such a conversion, two separate stores must be open at once. 298Both stores may be in the same {@link com.sleepycat.db.Environment}, if 299desired, by giving them different store names. But since all data is being 300rewritten, there are performance advantages to creating the new store in a new 301fresh environment: the data will be compacted as it is written, and the old 302store can be removed very quickly by deleting the old environment directory 303after the conversion is complete.</p> 304 305</body> 306</html> 307