Android Persistence Library

Compiler plugin for persistence layer and interprocess communication on Android

We provide a source-code generator for the Java and Android eco-system that enhances a given datamodel by database and peer-class functionality without the need of an additional library. This is done by class templates that are transformed to autogenerated code at compile time.

The application integrates with existing gradle and maven build environments and generates code with a clean interface for use within your application.

It is possible to create

  • SQLite database objects
  • Android DataProvider implementations that use the underlying schema
  • Parcelable code

The library is aware of combined primary key definitions, foreign key handling, embedded entities and database versioning for definition updates. All model definitions are done by source-only Annotations and create light-weight plain code without requiring any additional library dependency.

Android Room

The system has some benefits and drawbacks when compared to the room persistence library, it is currently not a source-compatible room alternative but is based on the same principle of autogenerated code which directly utilizes the SQLite API.
We have several aspects of the Room persistence library on the developer roadmap for upcoming versions, see a comparison of features below:

Supported by our library

  • It has support for foreign key handling with combined key fields
  • Versioned databases are very intuitive by setting an integer version on the annotation of the new item
    which may be either a field or class, the system then automatically handles upgrades
  • Primary keys which may also consist of multiple fields
  • Embedded objects with prefixes

Differences to the Room Persistence library

  • It currently does not support a query language as provided by Android Room;
    Instead constants are created that also reflect join correlations and make it easy to construct native queries
    (see the JOIN_CUSTOMER constant for an example on how Seife solves this)
  • Room assumes all fields are persisted unless the @Ignore annotation is added
    this lib will only persist fields annotated with @SeifeField (see e.g. Example 1)
  • It does not have a RoomDatabase instance which is fairly costly,
    instead a class extending from DbOpenHelper is created
  • No type converter is available yet, instead the conversion is performed inline in the generated read methods
  • There is no support for explicit definition of SQLite collations

What’s exceptional for the seife code generator

  • On-the-fly creation of DataProvider classes accessing the underlying SQLite database
  • Support to also create code for the parcelable interface
    allows for interprocess communication via the Android binder bridge and remote service calls with complex datatypes (.aidl)

We are always open for suggestions on improving the library and adding new functionality, so don’t hesitate to contact us if you have any questions
Our persistence library helps developers to write energy- and time-efficient code, reduces the need of writing error-prone boilerplate code and saves a lot of time so you can focus on your project.

Default methods aspect oriented programming on JPA entities

When working with entity classes it is common not to add additional logic to the objects. This avoids dependencies in the code to keep it modular and maintainable. With default methods it is possible to enhance the entities in a simple way without affecting the original notion of a business object that just keeps the data.

Usually the entities are loaded from a database via the persistence layer, and are then used within the runtime context of the application. Modern frameworks provide descriptive mechanisms to define which entity attributes are to be displayed within the controls of the user interface.

public class Person {

    @NotNull
    @Size(min = 3, max = 40)
    private String firstName;
    private String name;
    
    @NotNull
    private Date birthday;

}

If the Person entity is to be displayed in a GUI the programmer only declares the columns of the table with the names of the class attributes. Modern frameworks then automatically load the values from the databean and display them in the user interface.

  public Date getBirthday() {
     return birthday;
  }
  
  public int getAgeInYears() {
     long ageInMillis = System.currentTimeMillis() - getBirthday().getTime();
     Calendar cal = Calendar.getInstance();
     cal.setTimeInMillis(ageInMillis);
     return cal.get(Calendar.YEAR);
  }

In the above example the age of a person is an aspect of the person class that can be derived from the current date and the persons birthday. Adding such a method will enable the framework to also show the current age. However it is these kind of methods that make the code harder to maintain as they are usually scattered around in several places of code. At best the developers have managed to put them into a common utility class. (which is used as a library in different versions throughout the project..) .

To generally avoid adding logic to the entity class by introducing a getter as shown one declares it as an adapter in the GUI logic. That way the birthday is converted to the age by the presentation layer.

interface AgeAspect {
  
  Date getBirthday();
  
  /**
   * See also 
   * {@linkplain http://stackoverflow.com/questions/1116123/how-do-i-calculate-someones-age-in-java}
   * @return the age in years relative to today
   */
  default int getAgeInYears() {
     long ageInMillis = System.currentTimeMillis() - getBirthday().getTime();
     Calendar cal = Calendar.getInstance();
     cal.setTimeInMillis(ageInMillis);
     return cal.get(Calendar.YEAR);
  }	
}

public class Person implements AgeAspect {

    @NotNull
    @Size(min = 3, max = 40)
    private String firstName;
    private String name;
    
    @NotNull
    private Date birthday;

    public Date getBirthday() {
       return birthday;
    }
}

With the new default methods introduced in Java 8 one can enhance every class that has a getBirthday() method can be amended with the age aspect by simply implementing the interface and without breaking any existing inheritance hierarchies.

The Viritin library that adds functionality for JPA entities in the Vaadin web app gui framework will soon have support for default methods, see pull request here.

Currently most frameworks do not support calling the default methods and the introspection only determines normal properties of classes. However the regular reflection api introduced in Java 4 is fully sufficient to implement this functionality therefore the libraries supporting it are still compatible with older Java releases.

Hopefully more library implementations such as the apache commons beanutils will support default methods soon.

The right Java bytecode version for Android

Android up to API level 23 does not support all possibilities of Java 8, the classes that the compiler generates have a version flag to denote which bytecode extensions can be inside of it. If you ever happen to hit an error like this during your build

UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.RuntimeException: Exception parsing classes
        at com.android.dx.command.dexer.Main.processClass(Main.java:752)
        at com.android.dx.command.dexer.Main.processFileBytes(Main.java:718)
        at com.android.dx.command.dexer.Main.access$1200(Main.java:85)
        at com.android.dx.command.dexer.Main$FileBytesConsumer.processFileBytes(Main.java:1645)
        at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
        at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
        at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
        at com.android.dx.command.dexer.Main.processOne(Main.java:672)
        at com.android.dx.command.dexer.Main.processAllFiles(Main.java:574)
        at com.android.dx.command.dexer.Main.runMonoDex(Main.java:311)
        at com.android.dx.command.dexer.Main.run(Main.java:277)
        at com.android.dx.command.dexer.Main.main(Main.java:245)
        at com.android.dx.command.Main.main(Main.java:106)
Caused by: com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)
        at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)
        at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)
        at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)
        at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)
        at com.android.dx.command.dexer.Main.parseClass(Main.java:764)
        at com.android.dx.command.dexer.Main.access$1500(Main.java:85)
        at com.android.dx.command.dexer.Main$ClassParserTask.call(Main.java:1684)
        at com.android.dx.command.dexer.Main.processClass(Main.java:749)
        ... 12 more
1 error; aborting

:app:transformClassesWithDexForDebug FAILED

you most likely included a class generated with a compiler target-level that was too advanced ( 34 hexadecimal in the case above is the level 52 of the Java 8 class format).
Android as of today supports all features of the JDK 7 compiler (see e.g. the release notes of the ADT plugin http://developer.android.com/tools/sdk/eclipse-adt.html).

In order to actually find the classes that were compiled against the wrong Java version you can use this command and check for anything that is >= 52

javap -v *.class | grep major 
# it'll report e.g.:
  major version: 50

To define which Java bytecode compatibility level to use and still have the latest jdk8 in your JAVA_HOME, you can use this in the build section of your Maven pom:

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>

 

The Gradle Java plugin is configured like this (the compatibility definition needs to be after applying the java plugin to take effect and gradle-android projects have this stored in the compileOptions instead as shown below)

allprojects {
    apply plugin: 'java'
    sourceCompatibility = 1.7
    targetCompatibility = 1.7
}

// Gradle-Android projects have this stored in the compileOptions instead:
android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    // [..]
}

Source and target compatibility are usually set to the same level, you need to be careful and should know what you are doing if you set them to different values, for example linkage errors at runtime may occur if the classes refer to newer java apis that were not present earlier.

Spliterator for “in-between” lambda processing

The usecase of summing up the distances in a list of vectors can benefit from parallelization on todays multi-core systems with Java 8 lambdas using a Spliterator.

If one writes

vecList.stream().mapToDouble( e-> 
    e.getDistance( b )

Then a reference to the next point “b” isn’t there hence the calculation cannot be performed that way. There is a property of each list item, it has a relation to the previous and a relation to the next item in the list.

Expressed as a piece of java code it might look as simple as this

final class Relation<T> {
   private final T a;
   private final T b;
   public Relation(T t0, T t1) {
      this.a = t0;
      this.b = t1;
   }
   public T getA() {
      return a;
   }
   public T getB() {
      return b;
   }
}

If we iterate on the relation organizing the list items into pairs, a distance calculation is possible by using a lambda expression and the stream api.

double totalDistance = stream.mapToDouble(
   e -> {
      return e.getA().distance(e.getB());
   }).sum();

To get there, a Spliterator class has to be defined that provides items for processing when the lambda expression is evaluated and is also capable of dividing the remaining set of items into subsets (sub-lists in this case) for parallel processing. Here is a possible implementation:

/**
 * A provider for pairs of consecutive list items
 *
 * @param <T>
 */
public class PairIterator<T> implements Spliterator<Relation<T>> {

 private final List<T> list;

 private int index;
 private final int start;
 private int end;
 
 /**
  * Allows to iterate over elements and their successors
  */
 public PairIterator(List<T> list) {
    this(list, 0, list.size());
 }

 /**
  * Internally used when the iterator is being split
  */
 private PairIterator(List<T> list, int start, int end) {
    this.list = list;
    this.start = index = start;
    this.end = end;
 }
 /**
  * Creates a new Relation between two consecutive items and 
  * performs the {@link Consumer#accept(entry)} operation. 
  */ 
 @Override
 public boolean tryAdvance(Consumer<? super Relation<T>> action) {
    Relation<T> entry;
    if (index >= end || index >= list.size()-1) {
       return false;
    }
    entry = new Relation<T>(list.get(index), list.get(++index));
    action.accept(entry);
    return true;
 }

 /**
  * If the list still has more that 4 elements to be processed
  * the method splits the work items into two working sets of ordered elements
  */ 
 @Override
 public PairIterator<T> trySplit() {
    int size = end - index;
    if (size > 4) {
       //reuse the middle element for both sides, so pair(mid-1,mid) and pair(mid,mid+1) is created 
       int mid = start + (size >> 1);
       int prevEnd = end;
       end = mid;
       return new PairIterator<T>(list, mid, prevEnd);
    }
    return null;
 }
 /**
  * @return the size of the remaining workload
  */
 @Override
 public long estimateSize() {
    return (end - index);
 }
 /**
  * Configuration of this spliterator
  */
 @Override
 public int characteristics() {
    return Spliterator.CONCURRENT | Spliterator.SIZED | Spliterator.ORDERED;
 }
}

There are two methods in the Spliterator implementation that are used to accomplish streaming the relations of predeccessor and successor, tryAdvance and trySplit. The first actually performs the action on the item pair by instantiating a Relation  on-the-fly, and calling accept(...) where the real calculation happens. The latter contains the algorithm to distribute the workload to the consumers. If the size of the remaining elements is greater than 4 it changes the indeces pointing into the list and returns a new instance configured for the remaining indeces. The method is designed to have a list element being used in two relations, once as a sucessor and once as a predecessor, so care must be taken not to modify its state by executing code with side-effects.

To use it a Stream is constructed with the StreamSupport class by passing the Spliterator as an argument and true to ensure it is processed in parallel:

//creates a parallel stream
Stream<Relation<Vec2>> stream = StreamSupport.stream(new PairIterator<Vec2>(vecList), true);

double totalDistance = stream.mapToDouble(
  e -> {
     return e.getA().distance(e.getB());
  }).sum();

 

Generic interfaces and algorithms

A common pattern in object oriented programming is interfacing classes that have the same methods. If these classes encapsulate a datatype and expose  methods that take the datatype as a parameter, generic interfaces. The approach results in the ability to apply a generic algorithm on different encapsulated data types.

For example vector math classes have this property, they can be defined for double and float datatypes and also for 2- and 3-dimensional implementations with methods for doing common operations.

public final class Vec3 implements Vec<Vec3> {
  private double x;
  private double y;
  private double z;

  /**
   * returns the distance to another vector
   * @param other the other vector
   * @return distance
   */
   public final double distance(Vec3 other) {
      return Math.sqrt(
        (x-other.x)*(x-other.x)+
        (y-other.y)*(y-other.y)+
        (z-other.z)*(z-other.z));
   }
}

public final class Vec2 implements Vec<Vec2> {
  private double x;
  private double y;
 
  /**
   * returns the distance to another vector
   * @param other the other vector
   * @return distance
   */
   public final double distance(Vec2 other) {
      return Math.sqrt(
        (x-other.x)*(x-other.x)+
        (y-other.y)*(y-other.y));
   }
}

The generic interface that both classes implement can then be declared like this:

public interface Vec<V> {
   double distance(V other);
}

It can contain all functions that have the same ‘abstract’ method signature that the classes have in common, ie. the more complicated cross product that returns a vector again.

The real benefit of this is not immediately obvious, consider adding up the distances of a list of items – one would naively just implement the method for each vector type Vec2 and Vec3. This results in duplicate code, reduced maintainability and is errorprone.

A class holding the algorithms operating on the Vec<V> interface can be used instead, the method needs to make use of the disance(V) method to determine the overall distance.

public class Toolbox {
  /**
   * Determines the overall distance between the points in the list
   */
  public static <T extends Vec<T>> double getAllDistances(List<T> points) {
     T prevPoint = points.get(0);
     T point;
     double sum = 0.0;
     int len = points.size();
     for (int i=1;i<len; i++) {
        point = points.get(i);
        sum+=point.distance(prevPoint);
        prevPoint = point;
     }
     return sum;
  }
}

This method is type-safe and works with all types implementing the generic Vec interface due to the <T extends Vec<T>> declaration. That way algorithms on vectors can safely be defined once for several variations of the classes.

In contrast, a declaration like below seems more natural at first, but does not work:

 public static <T> double getAllDistances(List<Vec<T>> points) {
    Vec<T> prevPoint = points.get(0);
    Vec<T> point;
    double sum = 0.0;
    for (int i=1; i<points.size(); i++) {
       point = points.get(i);
       sum+=point.distance(prevPoint);
       prevPoint = point;
    }
    return sum;
 }

Here, calling the method point.distance(prevPoint) gives a compiler error since prevPoint is of type Vec<T> but the method signature is distance(V other) and expects it to be T, rather than Vec<T>.