Example 1 – one-to-many relation

The use-case ‘Customer has several orders’ can be modelled by two business objects Customer and Order. By using the source-level annotations the relation between the instance is defined declaratively just as it is usually done with JPA.

@SeifeClass(
        generatorOptions = {
                GeneratorOption.BOCLASS, GeneratorOption.SCHEMA_PEER,
                GeneratorOption.DB_HELPER+"=com.uc_mobileapps.seifesample01.db.CustomerDB",
                GeneratorOption.DATA_PROVIDER+"=com.uc_mobileapps.seifesample01.provider.CustomerProvider"
        },
        description = "A customer object, since this is a source annotation it won't occur in " +
        "the final class and has no runtime dependencies")
public class Customer {

    @SeifeField(isPrimaryKey = true,
        description="a simple primary key field")
    private Long id;

    @SeifeField
    private String name;

}

With @SeifeClass and @SeifeField the programmer determines to generate additional program code. Placing the annotations defines which class attributes are considered to be part of the data model.

With the generatorOptions annotation attribute the required android code type is chosen.

@SeifeClass(
        generatorOptions = {
        GeneratorOption.BOCLASS, GeneratorOption.SCHEMA_PEER},
        description="Defines a simple many to one relation")
public class Order {
    @SeifeField(isPrimaryKey = true)
    private Long id;

    @SeifeField(mandatory = true, sqlOptions = @SqlFieldOptions(sqlIndex = "idxOrderDate"))
    private Date orderDate;

  /**
   * The foreign key field 'fkField' refers to {@link #customer},
   * it will be set via {@link #setCustomer(Customer)}.
   */
    @SeifeField(foreignKey = @ForeignkeyDef(fkField = "customer", refKeyField="id", refClass=Customer.class))
    private Long customerId;

  private Customer customer;

  public Customer getCustomer() {
    return customer;
  }
}

A foreign key can be defined directly for a class attribute, the meta model is being complemented by the required android program code. This avoids error prone and time consuming development of ‘boiler-plate’ interface code, the programmer can focus on the business logic instead.

All table column names are available as static identifiers. Among other things this improves readability and finding references in the code. It also facilitates code refactoring.

The following code shows the generated SQL schema peer classes with the common logic to read and write via the  android.database.Cursor API.

The peer classes also contain the SQLite DDL definitions with the required data types to create the tables.

  • package com.uc_mobileapps.seifesample01.bo.schema;
    
    import com.uc_mobileapps.seifesample01.bo.Customer;
    
    import android.content.ContentResolver;
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.net.Uri;
    import java.util.List;
    import java.util.ArrayList;
    
    public class CustomerSchema { 
      //[begin seife autogenerated@
    
      /**
       * Table name of the Customer table
       */
      public static String TBL_CUSTOMER = "customer";
      
    
      /** 
       * a simple primary key field
       */
      public static String COL_ID = "id";
    
      public static String COL_NAME = "name";
      
      /**
       * All columns
       */
      public static String[] COLUMNS = new String[] { COL_ID, COL_NAME	};
    
      /**
       * Table creation script
       */
      private static final String SQL_CREATE_TABLE_CUSTOMER =
          "create table " + TBL_CUSTOMER + " (" + 
    
              COL_ID + " integer primary key autoincrement," +
              COL_NAME + " text" +
              ")";
    
    
      private static CustomerSchema schema = new CustomerSchema();
      public static CustomerSchema instance() {
        return schema;
      }
    
      /**
       * 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(Customer bo) {
        ContentValues contentValues = new ContentValues();
    
        if (bo.getId() != null) {
          contentValues.put(COL_ID, bo.getId());
        }
        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 Customer readFromCursor(Cursor cursorFrom, Customer bo)
      {
        if (bo == null) {
          bo = new Customer();
        }
        final Cursor c = cursorFrom; 
    
        bo.setId(c.isNull(c.getColumnIndex(COL_ID)) ? null : c.getLong(c.getColumnIndex(COL_ID)));
        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_CUSTOMER); 
        return result;
      }
      //@end seife autogenerated]
    }
    

  • package com.uc_mobileapps.seifesample01.bo.schema;
    
    import com.uc_mobileapps.seifesample01.bo.Order;
    
    import android.content.ContentResolver;
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.net.Uri;
    import java.util.List;
    import java.util.ArrayList;
    
    import com.uc_mobileapps.seifesample01.bo.Customer;
    import com.uc_mobileapps.seifesample01.bo.schema.OrderSchema;
    import java.util.Date;
    
    public class OrderSchema { 
      //[begin seife autogenerated@
    
      /**
       * Table name of the Order table
       */
      public static String TBL_ORDER = "order";
      
    
      public static String COL_ID = "id";
    
      public static String COL_FK_CUSTOMER_CUSTOMER_ID = "customerId";
    
      public static String COL_ORDER_DATE = "orderDate";
      
      /**
       * All columns
       */
      public static String[] COLUMNS = new String[] { COL_ID, COL_FK_CUSTOMER_CUSTOMER_ID, COL_ORDER_DATE	};
    
      /**
       * Table creation script
       */
      private static final String SQL_CREATE_TABLE_ORDER =
          "create table " + TBL_ORDER + " (" + 
    
              COL_ID + " integer primary key autoincrement," +
              COL_FK_CUSTOMER_CUSTOMER_ID + " integer," +
              COL_ORDER_DATE + " integer not null" +
              ", " +
              " FOREIGN KEY(" + COL_FK_CUSTOMER_CUSTOMER_ID  + ")" +
              " REFERENCES " + CustomerSchema.TBL_CUSTOMER 
              +"(" + CustomerSchema.COL_ID  + ")" +			 		
              ")";
    
    
      private static OrderSchema schema = new OrderSchema();
      public static OrderSchema instance() {
        return schema;
      }
    
      /**
       * Checks for constraints defined on the fields
       */
      public boolean checkConstraints(ContentValues values) {
        //text,scancode,meta
        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(Order bo) {
        ContentValues contentValues = new ContentValues();
    
        if (bo.getId() != null) {
          contentValues.put(COL_ID, bo.getId());
        }
        contentValues.put(COL_FK_CUSTOMER_CUSTOMER_ID, bo.getCustomerId());
        contentValues.put(COL_ORDER_DATE, (bo.getOrderDate()!=null) ? bo.getOrderDate().getTime() : null);
        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 Order readFromCursor(Cursor cursorFrom, Order bo)
      {
        if (bo == null) {
          bo = new Order();
        }
        final Cursor c = cursorFrom; 
    
        bo.setId(c.isNull(c.getColumnIndex(COL_ID)) ? null : c.getLong(c.getColumnIndex(COL_ID)));
        bo.setCustomerId(c.isNull(c.getColumnIndex(COL_FK_CUSTOMER_CUSTOMER_ID)) ? null : c.getLong(c.getColumnIndex(COL_FK_CUSTOMER_CUSTOMER_ID)));
        bo.setOrderDate(c.isNull(c.getColumnIndex(COL_ORDER_DATE)) ? null : new java.util.Date(c.getLong(c.getColumnIndex(COL_ORDER_DATE))));
        return bo;
      }
      
      /**
       * @return hard-coded table creation scripts
       */
      public List<String> getTableScripts() {
        List<String> result = new ArrayList<String>();
        result.add(SQL_CREATE_TABLE_ORDER); 
        return result;
      }
    
      /** 
       * Resolves the Customer instance for the Order#customer field
       * @param context the ContentResolver is obtained from there
       * @param bo the business object to get the foreign key reference from
       * @return
       */
      public Customer resolveCustomerField(Context context, Order bo) {
        ContentResolver contentResolver = context.getContentResolver();
        String selection = null;
        String[] selectionArgs = null;
    
        Uri uri = com.uc_mobileapps.seifesample01.provider.CustomerProvider.getContentUriCustomer().buildUpon()
            .appendQueryParameter(CustomerSchema.COL_ID, String.valueOf(bo.getId()))
            .build();
        Cursor cursor = contentResolver.query(uri, CustomerSchema.COLUMNS, selection, selectionArgs, null);
        try {
          Customer foreignInstance = 
              CustomerSchema.instance().readFromCursor(cursor, new Customer());
          //bo.setCustomer(foreignInstance);
          return foreignInstance;
        } finally {
          cursor.close();
        }
      }
      //@end seife autogenerated]	
    }