Versionierung von Tabellen und Spalten

Beide Annotationen @SeifeClass und @SeifeField haben ein Versionsattribut. Es definiert ab welcher Modellversion das BO oder Datenfeld erstmals verfügbar war.
Wann immer die App von einer vorherigen Datenbankversion aktualisiert wird, werden die nötigen Tabellenskripte ausgeführt um auf das neueste Modell zu migrieren. Dies geschieht mit dem von Android empfohlenen Standard mechanismus SQLOpenHelper. Das Gleiche funktioniert für mit  @SeifeField annotierte Attribute. Wenn eine Menge Felder dem Datenmodell hinzugefügt werden sollen, fügt man sie mit der gleichen neuen Versionsnummer hinzu. Diese muss natürlich größer sein als die bisherig vergebenen, auch sollte sie nicht mehr geändert werden um die Kompatibilität zwischen verschiedenen Versionen beizubehalten.

Wenn eine Klasse eine Version in der @SeifeClass Annotation hat, müssen die Versionen später hinzugefügter Attribute größer als diese sein.

Der erzeugte DBOpenHelper wird die  create() Methode erstellen, die das aktuelle Datenmodell erzeugt. Ferner stellt es die update() Methode bereit, die die benötigte Logik enthält um die verschiedenen Versionen  zu aktualisieren. Datenbank-Indezes und -Constraints von neuen Klassenattributen können für für die Spaltenupdates nicht generiert werden und müssen manuell erzeugt werden.
Wenn allerdings in einer neuen Version eine komplette Klasse hinzugefügt wird, so wird das passende create table .. Skript aufgerufen und die Tabellen mit Constraints und Index erstellt.

Beispiel
Die Klasse Language ist in Version 2 hinzugekommen. In Version 3 wurde eine neue Eigenschaft “description” hinzugefügt.

@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;
}

Der für den SQLiteOpenHelper erzeugte zugehörige Code und die passende Klasse mit den SQL-Schema Definitionen

  • /**
     * 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]
    }