fuin.org

Small Open Source Java Tools and Libraries

Example story of an evolving class

First version of your class

package my.test;

public class Address implements Serializable {

    private static final long serialVersionUID = -7393761964342362874L;

    private long versionUID = serialVersionUID;

    private String name;
    
    // ... setter/getter and other methods ...
    
}

You serialize some instances of this class to your hard disk.

Now you want to change the class and split the name field into a first name and last name. But what about the serialized versions on disk? If you change the class you cannot read them anymore!

The solution...

From version 1 to version 2

  1. Make a copy of the class and move it into a separate package like "my.test.old.address.v1" - If you use the VersioningJavaSerializer Internal link outside layout it's important not to change the name of the class itself (only the package). The Java deserialization process checks if the class names are equal and will fail otherwise! For other VersioningSerializer Internal link outside layout implementations this is not crucial but it's a good practice to use this naming scheme for all versioning serializers.
  2. Change the class and assign a new serialVersionUID:
            package my.test;
            
            public class Address implements Serializable {
            
                private static final long serialVersionUID = -2466841694916192299L;
            
                private long versionUID = serialVersionUID;
            
                private String firstName;
            
                private String lastName;
            
                // ... setter/getter and other methods ...
                
            }
       
    
  3. Create a converter that converts an instance of the old class into the new version and put it in the same package as the old class (my.test.old.address.v1):
            package my.test.old.address.v1;
            
            /**
             * Converts version 1 into version 2.
             */
            public class AddressConverter implements Converter<my.test.old.address.v1.Address, my.test.Address> {
            
                @Override
                public final my.test.Address convert(final my.test.old.address.v1.Address src) {
                    if (src == null) {
                        return null;
                    }
                    final String firstName;
                    final String lastName;
                    final String name = src.getName();
                    if (name == null) {
                        firstName = null;
                        lastName = null;
                    } else {
                        final int p = name.indexOf(" ");
                        if (p == -1) {
                            firstName = null;
                            lastName = name;
                        } else {
                            firstName = name.substring(0, p);
                            lastName = name.substring(p + 1);
                        }
                    }
                    return new my.test.Address(firstName, lastName);
                }
            
            }
       
    
  4. Add a new entry to the history XML:
        <?xml version="1.0" encoding="UTF-8"?>
        <history versionTag="versionUID">  
          <class package="my.test" name="Address">
            <version>
              <serialVersionUID>-7393761964342362874</serialVersionUID>
              <oldClass>my.test.old.address.v1.Address</oldClass>
              <converterClass>my.test.old.address.v1.AddressConverter</converterClass>
            </version>
          </class>
        </history>
       
    

You serialize again some instances of the changed class to your hard disk.

The class changes again by adding a unique identifier!

From version 2 to version 3

  1. Make a copy of the class and move it into a separate package like "my.test.old.address.v2"
  2. Change the class and assign a new serialVersionUID:
            package my.test;
            
            public class Address implements Serializable {
            
                private static final long serialVersionUID = 5570679541850483841L;
            
                private long versionUID = serialVersionUID;
            
                private int id;
            
                private String firstName;
            
                private String lastName;
            
                // ... setter/getter and other methods ...
                
       
    
  3. Create a converter that converts version 2 to version 3:
            package my.test.old.address.v2;
            
            /**
             * Converts version 2 into version 3.
             */
            public class AddressConverter implements Converter<my.test.old.address.v2.Address, my.test.Address> {
            
                private IdFactory idFactory;
            
                /**
                 * Constructor with ID factory.
                 * 
                 * @param idFactory
                 *            ID factory to use.
                 */
                public AddressConverter(final IdFactory idFactory) {
                    super();
                    this.idFactory = idFactory;
                }
            
                @Override
                public final my.test.Address convert(final my.test.old.address.v2Address src) {
                    if (src == null) {
                        return null;
                    }
                    return new my.test.Address(idFactory.nextId(), src.getFirstName(), src.getLastName());
                }
            
            }
       
    
  4. Add a new entry to the history XML:
            <?xml version="1.0" encoding="UTF-8"?>
            <history versionTag="versionUID">  
              <class package="my.test" name="Address">
                <version>
                  <serialVersionUID>-7393761964342362874</serialVersionUID>
                  <oldClass>my.test.old.address.v1.Address</oldClass>
                  <converterClass>my.test.old.address.v1.AddressConverter</converterClass>
                </version>
                <version>
                  <serialVersionUID>-2466841694916192299</serialVersionUID>
                  <oldClass>my.test.old.address.v2.Address</oldClass>
                  <converterClass>my.test.old.address.v2.AddressConverter</converterClass>
                </version>
              </class>
            </history>
       
    
That's it! The deserializer is now able to handle all three serialized versions of the class.