Table and Column Versioning

Both Annotations @SeifeClass and @SeifeField have a version attribute. The version level declares since when the business object or field was first available.

Whenever an app is upgraded from a previous database version the table creation is performed to migrate to the newest level via an implementation of the default SQLOpenHelper mechanism.
The same works for attributes annotated with @SeifeField. Whenever you add a set of fields to your BO model, add the same version level to all of them with a value greater than the highest currently available.
Once a version is set on either the class or a property it should not be changed anymore to keep compatibility among different deployed versions.
If a class has a version in the @SeifeClass annotation, the versions of properties introduced later must be greater than the class version.

The generated DBOpenHelper will populate the create()
method with the latest BO model. The update() method will contain the required logic to pull up between different version levels. Indexes and constraints of properties will not be generated in the column update scripts and have to be created manually.
If a whole class is added within a version however, the table creation script is called and constraints and indexes are created. Code for downgrading is not being created and there currently is no support to drop existing columns.

Example
The class Language was added in version 2 and in version 3 a new attribute “description” was introduced.

@SeifeClass(sqlTablename="locale", version=2, generatorOptions={GeneratorOption.BOCLASS, GeneratorOption.SCHEMA_PEER, GeneratorOption.DB_HELPER+"=DBHelper"})
public class Language {

  @SeifeField(isPrimaryKey=true, sqlOptions=@SqlFieldOptions(sqlAutoIncrement=false, sqlColumn="_country"))
  private String countryId;
  @SeifeField(isPrimaryKey=true, sqlOptions=@SqlFieldOptions(sqlAutoIncrement=false, sqlColumn="_language"))
  private String languageId;

  @SeifeField(version=2)
  private String name;

  @SeifeField(version=3)
  private String description;

  @SeifeField(mandatory = true,version=2)
  private String flag;
}

Below is the generated code for SQLiteOpenHelper and the class with the generated schema definitions

  • /**
     * Database helper that creates and/or upgrades the schema
     */
    public class DBHelper extends SQLiteOpenHelper
    {
      /**
       * Name of the database
       */
      private static final String DB_NAME = "DBHelper.db";
    
      /**
       * The version of this database for the current release
       */
      private static final int DB_VERSION = 3;
    
      /**
       * Default constructor for the helper
       */
      public DBHelper(Context paramContext) {
        this(paramContext, DB_NAME, null, DB_VERSION);
      }
    
      protected DBHelper(Context paramContext, String paramString, SQLiteDatabase.CursorFactory paramCursorFactory, int paramInt) {
        super(paramContext, paramString, paramCursorFactory, paramInt);
      }
    
      @Override
      public void onCreate(SQLiteDatabase db) {
        seifeCreate(db);
      }
    
      @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        seifeUpgrade(db, oldVersion, newVersion);
      }
    
      //[begin seife autogenerated@
    
      /**
       * Outsourced data creation logic of automatically created tables, see {@see #onCreate(SQLiteDatabase)}
       */
      public void seifeCreate(SQLiteDatabase db) { 
        for (String ddl : LanguageSchema.instance().getTableScripts()) {
          db.execSQL(ddl);
        }
      }
      
      /**
       * Outsourced data update logic of automatically created tables, see {@see #onUpgrade(SQLiteDatabase,int,int)}
       */
      public void seifeUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion < 2 && newVersion >= 2) { 
          for (String ddl : LanguageSchema.instance().getTableScripts()) {
            db.execSQL(ddl);
          }
        }
        if (oldVersion < 3 && newVersion >= 3) { 
          db.execSQL("ALTER TABLE " + LanguageSchema.TBL_LANGUAGE + " ADD COLUMN " + LanguageSchema.COL_DESCRIPTION + " text");
        }
      }
    
      //@end seife autogenerated]
    
    }
    

  • public class LanguageSchema { 
      //[begin seife autogenerated@
    
      /**
       * Table name of the Language table
       */
      public static String TBL_LANGUAGE = "locale";
      
    
      public static String COL_COUNTRY_ID = "_country";
    
      public static String COL_LANGUAGE_ID = "_language";
    
      public static String COL_DESCRIPTION = "description";
    
      public static String COL_FLAG = "flag";
    
      public static String COL_NAME = "name";
      
      /**
       * All columns
       */
      public static String[] COLUMNS = new String[] { COL_COUNTRY_ID, COL_LANGUAGE_ID, COL_DESCRIPTION, COL_FLAG, COL_NAME	};
    
      /**
       * Table creation script
       */
      private static final String SQL_CREATE_TABLE_LANGUAGE =
          "create table " + TBL_LANGUAGE + " (" + 
    
              COL_COUNTRY_ID + " text primary key," +
              COL_LANGUAGE_ID + " text primary key," +
              COL_DESCRIPTION + " text," +
              COL_FLAG + " text not null," +
              COL_NAME + " text" +
              ")";
    
      private static LanguageSchema schema = new LanguageSchema();
      public static LanguageSchema instance() {
        return schema;
      }
    
      /**
       * Checks for mandatory constraints defined on fields
       */
      public boolean checkConstraints(ContentValues contentValues) {
        return true;
      }
      
      /**
       * Gets all attribute values of the bo as key value pairs
       * @param bo may not be null
       * @return new instance of {@link ContentValues}
       */
      public ContentValues getContentValues(Language bo) {
        ContentValues contentValues = new ContentValues();
    
        if (bo.getCountryId() != null) {
          contentValues.put(COL_COUNTRY_ID, bo.getCountryId());
        }
        if (bo.getLanguageId() != null) {
          contentValues.put(COL_LANGUAGE_ID, bo.getLanguageId());
        }
        contentValues.put(COL_DESCRIPTION, bo.getDescription());
        contentValues.put(COL_FLAG, bo.getFlag());
        contentValues.put(COL_NAME, bo.getName());
        return contentValues;
      }
    
      /**
       * Sets all attributes from the cursor
       * @param cursorFrom the cursor to read from
       * @param bo may be null
       * @return the bo passed as a parameter or a new instance
       */
      public Language readFromCursor(Cursor cursorFrom, Language bo)
      {
        if (bo == null) {
          bo = new Language();
        }
        final Cursor c = cursorFrom; 
    
        bo.setCountryId(c.getString(c.getColumnIndex(COL_COUNTRY_ID)));
        bo.setLanguageId(c.getString(c.getColumnIndex(COL_LANGUAGE_ID)));
        bo.setDescription(c.getString(c.getColumnIndex(COL_DESCRIPTION)));
        bo.setFlag(c.getString(c.getColumnIndex(COL_FLAG)));
        bo.setName(c.getString(c.getColumnIndex(COL_NAME)));
        return bo;
      }
      
      /**
       * @return hard-coded table creation scripts
       */
      public List<String> getTableScripts() {
        List<String> result = new ArrayList<String>();
        result.add(SQL_CREATE_TABLE_LANGUAGE); 
        return result;
      }
      //@end seife autogenerated]	
    }