java.io.Serializable
public interface Conversion
extends java.io.Serializable
The Conversion
interface is implemented by the user. A
Conversion
instance is passed to the Converter(java.lang.String, int, java.lang.String, com.sleepycat.persist.evolve.Conversion)
constructor.
The Conversion
interface extends Serializable
and the
Conversion
instance is serialized for storage using standard Java
serialization. Normally, the Conversion
class should only have
transient fields that are initialized in the initialize(com.sleepycat.persist.model.EntityModel)
method.
While non-transient fields are allowed, care must be taken to only include
fields that are serializable and will not pull in large amounts of data.
When a class conversion is specified, two special considerations apply:
The Conversion
class must implement the standard equals method.
See equals(java.lang.Object)
for more information.
Conversions of simple types are generally simple. For example, a
String
field that contains only integer values can be easily converted to
an int
field:
// The old class. Version 0 is implied. // @Persistent class Address { String zipCode; ... } // The new class. A new version number must be assigned. // @Persistent(version=1) class Address { int zipCode; ... } // The conversion class. // class MyConversion1 implements Conversion { public void initialize(EntityModel model) { // No initialization needed. } public Object convert(Object fromValue) { return Integer.valueOf((String) fromValue); }@Override
public boolean equals(Object o) { return o instanceof MyConversion1; } } // Create a field converter mutation. // Converter converter = new Converter(Address.class.getName(), 0, "zipCode", new MyConversion1()); // Configure the converter as describedhere
.
A conversion may perform arbitrary transformations on an object. For example, a conversion may transform a single String address field into an Address object containing four fields for street, city, state and zip code.
// The old class. Version 0 is implied. // @Entity class Person { String address; ... } // The new class. A new version number must be assigned. // @Entity(version=1) class Person { Address address; ... } // The new address class. // @Persistent class Address { String street; String city; String state; int zipCode; ... } class MyConversion2 implements Conversion { private transient RawType addressType; public void initialize(EntityModel model) { addressType = model.getRawType(Address.class.getName()); } public Object convert(Object fromValue) { // Parse the old address and populate the new address fields // String oldAddress = (String) fromValue; Map<String, Object> addressValues = new HashMap<String, Object>(); addressValues.put("street", parseStreet(oldAddress)); addressValues.put("city", parseCity(oldAddress)); addressValues.put("state", parseState(oldAddress)); addressValues.put("zipCode", parseZipCode(oldAddress)); // Return new raw Address object // return new RawObject(addressType, addressValues, null); }@Override
public boolean equals(Object o) { return o instanceof MyConversion2; } private String parseStreet(String oldAddress) { ... } private String parseCity(String oldAddress) { ... } private String parseState(String oldAddress) { ... } private Integer parseZipCode(String oldAddress) { ... } } // Create a field converter mutation. // Converter converter = new Converter(Person.class.getName(), 0, "address", new MyConversion2()); // Configure the converter as describedhere
.
Note that when a conversion returns a RawObject
, it must return
it with a RawType
that is current as defined by the current class
definitions. The proper types can be obtained from the EntityModel
in the conversion's initialize
method.
A variation on the example above is where several fields in a class (street, city, state and zipCode) are converted to a single field (address). In this case a class converter rather than a field converter is used.
// The old class. Version 0 is implied. // @Entity class Person { String street; String city; String state; int zipCode; ... } // The new class. A new version number must be assigned. // @Entity(version=1) class Person { Address address; ... } // The new address class. // @Persistent class Address { String street; String city; String state; int zipCode; ... } class MyConversion3 implements Conversion { private transient RawType newPersonType; private transient RawType addressType; public void initialize(EntityModel model) { newPersonType = model.getRawType(Person.class.getName()); addressType = model.getRawType(Address.class.getName()); } public Object convert(Object fromValue) { // Get field value maps for old and new objects. // RawObject person = (RawObject) fromValue; Map<String, Object> personValues = person.getValues(); Map<String, Object> addressValues = new HashMap<String, Object>(); RawObject address = new RawObject(addressType, addressValues, null); // Remove the old address fields and insert the new one. // addressValues.put("street", personValues.remove("street")); addressValues.put("city", personValues.remove("city")); addressValues.put("state", personValues.remove("state")); addressValues.put("zipCode", personValues.remove("zipCode")); personValues.put("address", address); return new RawObject(newPersonType, personValues, person.getSuper()); }@Override
public boolean equals(Object o) { return o instanceof MyConversion3; } } // Create a class converter mutation. // Converter converter = new Converter(Person.class.getName(), 0, new MyConversion3()); // Configure the converter as describedhere
.
A conversion can also handle changes to class hierarchies. For example, if a "name" field originally declared in class A is moved to its superclass B, a conversion can move the field value accordingly:
// The old classes. Version 0 is implied. // @Persistent class A extends B { String name; ... } @Persistent abstract class B { ... } // The new classes. A new version number must be assigned. // @Persistent(version=1) class A extends B { ... } @Persistent(version=1) abstract class B { String name; ... } class MyConversion4 implements Conversion { private transient RawType newAType; private transient RawType newBType; public void initialize(EntityModel model) { newAType = model.getRawType(A.class.getName()); newBType = model.getRawType(B.class.getName()); } public Object convert(Object fromValue) { RawObject oldA = (RawObject) fromValue; RawObject oldB = oldA.getSuper(); Map<String, Object> aValues = oldA.getValues(); Map<String, Object> bValues = oldB.getValues(); bValues.put("name", aValues.remove("name")); RawObject newB = new RawObject(newBType, bValues, oldB.getSuper()); RawObject newA = new RawObject(newAType, aValues, newB); return newA; }@Override
public boolean equals(Object o) { return o instanceof MyConversion4; } } // Create a class converter mutation. // Converter converter = new Converter(A.class.getName(), 0, new MyConversion4()); // Configure the converter as describedhere
.
A conversion may return an instance of a different class entirely, as long as it conforms to current class definitions and is the type expected in the given context (a subtype of the old type, or a type compatible with the new field type). For example, a field that is used to discriminate between two types of objects could be removed and replaced by two new subclasses:
// The old class. Version 0 is implied. // @Persistent class Pet { boolean isCatNotDog; ... } // The new classes. A new version number must be assigned to the Pet class. // @Persistent(version=1) class Pet { ... } @Persistent class Cat extends Pet { ... } @Persistent class Dog extends Pet { ... } class MyConversion5 implements Conversion { private transient RawType newPetType; private transient RawType dogType; private transient RawType catType; public void initialize(EntityModel model) { newPetType = model.getRawType(Pet.class.getName()); dogType = model.getRawType(Dog.class.getName()); catType = model.getRawType(Cat.class.getName()); } public Object convert(Object fromValue) { RawObject pet = (RawObject) fromValue; Map<String, Object> petValues = pet.getValues(); Boolean isCat = (Boolean) petValues.remove("isCatNotDog"); RawObject newPet = new RawObject(newPetType, petValues, pet.getSuper()); RawType newSubType = isCat ? catType : dogType; return new RawObject(newSubType, Collections.emptyMap(), newPet); }@Override
public boolean equals(Object o) { return o instanceof MyConversion5; } } // Create a class converter mutation. // Converter converter = new Converter(Pet.class.getName(), 0, new MyConversion5()); // Configure the converter as describedhere
.
The primary limitation of a conversion is that it may access at most a single entity instance at one time. Conversions involving multiple entities at once may be made by performing a store conversion.
Class Evolution
Modifier and Type | Method | Description |
---|---|---|
java.lang.Object |
convert(java.lang.Object fromValue) |
Converts an old version of an object value to conform to the current
class or field definition.
|
boolean |
equals(java.lang.Object other) |
The standard
equals method that must be implemented by
conversion class. |
void |
initialize(EntityModel model) |
Initializes the conversion, allowing it to obtain raw type information
from the entity model.
|
void initialize(EntityModel model)
model
- the EntityModel.java.lang.Object convert(java.lang.Object fromValue)
If a RuntimeException
is thrown by this method, it will be
thrown to the original caller. Similarly, a IllegalArgumentException
will be thrown to the original caller if the
object returned by this method does not conform to current class
definitions.
The class of the input and output object may be one of the simple
types or RawObject
. For primitive types, the primitive wrapper
class is used.
fromValue
- the object value being converted. The type of this
value is defined by the old class version that is being converted.boolean equals(java.lang.Object other)
equals
method that must be implemented by
conversion class.
When mutations are specified when opening a store, the specified and
previously stored mutations are compared for equality. If they are
equal, there is no need to replace the existing mutations in the stored
catalog. To accurately determine equality, the conversion class must
implement the equals
method.
If the equals
method is not explicitly implemented by the
conversion class or a superclass other than Object
,
IllegalArgumentException
will be thrown when the store is opened.
Normally whenever equals
is implemented the hashCode
method should also be implemented to support hash sets and maps.
However, hash sets and maps containing Conversion
objects
are not used by the DPL and therefore the DPL does not require
hashCode
to be implemented.
equals
in class java.lang.Object
Copyright (c) 1996, 2020 Oracle and/or its affiliates. All rights reserved.