Skip to content

@BindDataSource

xcesco edited this page May 2, 2018 · 8 revisions

This annotation decorate an interface to define a datasource associated to a database schema. Between its attributes there is a DAO collection. Every DAO is defined by an interface annotated with @BindDao. Every DAO and is associated to a specific Java class which is associated to a specific table.

A data source class name have to finish with DataSource suffix.

Attributes

List of attributes is:

  • asyncTask: if true, generate async task name Bind<data source name without DataSource prefix>AsyncTask
  • cursorWrapper: if true, generate a wrapped cursor for every Java class managed by data-source. Cursor's name is Bind<data source name without DataSource prefix>Cursor.
  • daoSet: the collection of DAO associated to data-source.
  • fileName: filename used to store database
  • rx: enable reactive programming support. It's necessary to include in project RX dependencies.
  • log: controls generation of the log of SQL on logcat.
  • typeAdapters: Global sql-type-adapters. These adapters are applied to every property that adapter supports. See Global-SQL-Type-adapter for more information.
  • version: database version. The default version is 1.

Usage

Consider this interface to define a data source:

@BindDataSource(daoSet = {PersonDao.class}, fileName = "feature01.db")
public interface SampleDataSource {
}

With this interface, it is defined a data source name SampleDataSource and is referred a DAO named PersonDao.

When Kripton annotation processor process interface SampleDataSource , it generate a class name BindSampleDataSource similar to:

/**
 * <p>
 * Represents implementation of datasource SampleDataSource.
 * This class expose database interface through Dao attribute.
 * </p>
 *
 * <p><strong>This class is generated by Kripton Annotation Processor (3.0.2)</strong></p>
 *
 * @since Thu Nov 30 22:40:47 CET 2017
 * @see SampleDataSource
 * @see BindSampleDaoFactory
 * @see PersonDao
 * @see PersonDaoImpl
 * @see Person
 */
public class BindSampleDataSource extends AbstractDataSource implements BindSampleDaoFactory, SampleDataSource {
  /**
   * <p>datasource singleton</p>
   */
  static BindSampleDataSource instance;

  /**
   * <p>dao instance</p>
   */
  protected PersonDaoImpl personDao = new PersonDaoImpl(this);

  /**
   * Used only in transactions (that can be executed one for time */
  private final DataSourceSingleThread _daoFactorySingleThread = new DataSourceSingleThread();

  protected BindSampleDataSource(DataSourceOptions options) {
    super("feature01.db", 1, options);
  }

  @Override
  public PersonDaoImpl getPersonDao() {
    return personDao;
  }

  /**
   * <p>Executes a transaction. This method <strong>is thread safe</strong> to avoid concurrent problems. Thedrawback is only one transaction at time can be executed. The database will be open in write mode. This method uses default error listener to intercept errors.</p>
   *
   * @param transaction
   * 	transaction to execute
   */
  public void execute(Transaction transaction) {
    execute(transaction, onErrorListener);
  }

  /**
   * <p>Executes a transaction. This method <strong>is thread safe</strong> to avoid concurrent problems. Thedrawback is only one transaction at time can be executed. The database will be open in write mode.</p>
   *
   * @param transaction
   * 	transaction to execute
   * @param onErrorListener
   * 	error listener
   */
  public void execute(Transaction transaction, AbstractDataSource.OnErrorListener onErrorListener) {
    boolean needToOpened=!this.isOpenInWriteMode();
    @SuppressWarnings("resource")
    SQLiteDatabase connection=needToOpened ? openWritableDatabase() : database();
    try {
      connection.beginTransaction();
      if (transaction!=null && TransactionResult.COMMIT == transaction.onExecute(_daoFactorySingleThread.bindToThread())) {
        connection.setTransactionSuccessful();
      }
    } catch(Throwable e) {
      Logger.error(e.getMessage());
      e.printStackTrace();
      if (onErrorListener!=null) onErrorListener.onError(e);
    } finally {
      try {
        connection.endTransaction();
      } catch (Throwable e) {
        Logger.warn("error closing transaction %s", e.getMessage());
      }
      if (needToOpened) { close(); }
    }
  }

  /**
   * <p>Executes a batch opening a read only connection. This method <strong>is thread safe</strong> to avoid concurrent problems.</p>
   *
   * @param commands
   * 	batch to execute
   */
  public <T> T executeBatch(Batch<T> commands) {
    return executeBatch(commands, false);
  }

  /**
   * <p>Executes a batch. This method <strong>is thread safe</strong> to avoid concurrent problems. Thedrawback is only one transaction at time can be executed. if <code>writeMode</code> is set to false, multiple batch operations is allowed.</p>
   *
   * @param commands
   * 	batch to execute
   * @param writeMode
   * 	true to open connection in write mode, false to open connection in read only mode
   */
  public <T> T executeBatch(Batch<T> commands, boolean writeMode) {
    boolean needToOpened=writeMode?!this.isOpenInWriteMode(): !this.isOpen();
    if (needToOpened) { if (writeMode) { openWritableDatabase(); } else { openReadOnlyDatabase(); }}
    try {
      if (commands!=null) {
        return commands.onExecute(new DataSourceSingleThread());
      }
    } catch(Throwable e) {
      Logger.error(e.getMessage());
      e.printStackTrace();
      throw(e);
    } finally {
      if (needToOpened) { close(); }
    }
    return null;
  }

  /**
   * instance
   */
  public static synchronized BindSampleDataSource instance() {
    if (instance==null) {
      instance=new BindSampleDataSource(null);
    }
    return instance;
  }

  /**
   * Retrieve data source instance and open it.
   * @return opened dataSource instance.
   */
  public static BindSampleDataSource open() {
    BindSampleDataSource instance=instance();
    instance.openWritableDatabase();
    return instance;
  }

  /**
   * Retrieve data source instance and open it in read only mode.
   * @return opened dataSource instance.
   */
  public static BindSampleDataSource openReadOnly() {
    BindSampleDataSource instance=instance();
    instance.openReadOnlyDatabase();
    return instance;
  }

  /**
   * onCreate
   */
  @Override
  public void onCreate(SQLiteDatabase database) {
    // generate tables
    // log section BEGIN
    if (this.logEnabled) {
      Logger.info("Create database '%s' version %s",this.name, this.getVersion());
    }
    // log section END
    // log section BEGIN
    if (this.logEnabled) {
      Logger.info("DDL: %s",PersonTable.CREATE_TABLE_SQL);
    }
    // log section END
    database.execSQL(PersonTable.CREATE_TABLE_SQL);
    // if we have a populate task (previous and current are same), try to execute it
    if (options.updateTasks != null) {
      SQLiteUpdateTask task = findPopulateTaskList(database.getVersion());
      if (task != null) {
        // log section BEGIN
        if (this.logEnabled) {
          Logger.info("Begin update database from version %s to %s", task.previousVersion, task.currentVersion);
        }
        // log section END
        task.execute(database);
        // log section BEGIN
        if (this.logEnabled) {
          Logger.info("End update database from version %s to %s", task.previousVersion, task.currentVersion);
        }
        // log section END
      }
    }
    if (options.databaseLifecycleHandler != null) {
      options.databaseLifecycleHandler.onCreate(database);
    }
  }

  /**
   * onUpgrade
   */
  @Override
  public void onUpgrade(SQLiteDatabase database, int previousVersion, int currentVersion) {
    // log section BEGIN
    if (this.logEnabled) {
      Logger.info("Update database '%s' from version %s to version %s",this.name, previousVersion, currentVersion);
    }
    // log section END
    // if we have a list of update task, try to execute them
    if (options.updateTasks != null) {
      List<SQLiteUpdateTask> tasks = buildTaskList(previousVersion, currentVersion);
      for (SQLiteUpdateTask task : tasks) {
        // log section BEGIN
        if (this.logEnabled) {
          Logger.info("Begin update database from version %s to %s", task.previousVersion, task.currentVersion);
        }
        // log section END
        task.execute(database);
        // log section BEGIN
        if (this.logEnabled) {
          Logger.info("End update database from version %s to %s", task.previousVersion, task.currentVersion);
        }
        // log section END
      }
    } else {
      // drop all tables
      SQLiteUpdateTaskHelper.dropTablesAndIndices(database);

      // generate tables
      // log section BEGIN
      if (this.logEnabled) {
        Logger.info("DDL: %s",PersonTable.CREATE_TABLE_SQL);
      }
      // log section END
      database.execSQL(PersonTable.CREATE_TABLE_SQL);
    }
    if (options.databaseLifecycleHandler != null) {
      options.databaseLifecycleHandler.onUpdate(database, previousVersion, currentVersion, true);
    }
  }

  /**
   * onConfigure
   */
  @Override
  public void onConfigure(SQLiteDatabase database) {
    // configure database
    if (options.databaseLifecycleHandler != null) {
      options.databaseLifecycleHandler.onConfigure(database);
    }
  }

  public void clearCompiledStatements() {
    PersonDaoImpl.clearCompiledStatements();
  }

  /**
   * Build instance.
   * @return dataSource instance.
   */
  public static synchronized BindSampleDataSource build(DataSourceOptions options) {
    if (instance==null) {
      instance=new BindSampleDataSource(options);
    }
    instance.openWritableDatabase();
    instance.close();
    return instance;
  }

  /**
   * Build instance with default config.
   */
  public static synchronized BindSampleDataSource build() {
    return build(DataSourceOptions.builder().build());
  }

  /**
   * Rapresents transational operation.
   */
  public interface Transaction extends AbstractDataSource.AbstractExecutable<BindSampleDaoFactory> {
    /**
     * Execute transation. Method need to return {@link TransactionResult#COMMIT} to commit results
     * or {@link TransactionResult#ROLLBACK} to rollback.
     * If exception is thrown, a rollback will be done.
     *
     * @param daoFactory
     * @return
     * @throws Throwable
     */
    TransactionResult onExecute(BindSampleDaoFactory daoFactory);
  }

  /**
   * Rapresents batch operation.
   */
  public interface Batch<T> {
    /**
     * Execute batch operations.
     *
     * @param daoFactory
     * @throws Throwable
     */
    T onExecute(BindSampleDaoFactory daoFactory);
  }

  class DataSourceSingleThread implements BindSampleDaoFactory {
    private SQLContextSingleThreadImpl _context;

    private PersonDaoImpl _personDao;

    DataSourceSingleThread() {
      _context=new SQLContextSingleThreadImpl(BindSampleDataSource.this);
    }

    /**
     *
     * retrieve dao PersonDao
     */
    public PersonDaoImpl getPersonDao() {
      if (_personDao==null) {
        _personDao=new PersonDaoImpl(_context);
      }
      return _personDao;
    }

    public DataSourceSingleThread bindToThread() {
      _context.bindToThread();
      return this;
    }
  }
}

Generated data source class derived from AbstractDataSource that derive from SQLiteOpenHelper.

To use a data source in a client application, just retrieve a reference to singleton instance of data source

BindSampleDataSource dataSource = BindSampleDataSource .instance();

To execute a transaction, just invoke the following code

dataSource.execute(new Transaction() {

  @Override
  public TransationResultonExecute(BindSampleDaoFactory daoFactory) {
    DaoPersonImpl dao = daoFactory.getPersonDao();

    long result = dao.insertRaw1("test", 52);
    dao.insertRaw2("test2", 23)
    
    // commit transaction
    return TransationResult.COMMIT;
  }

});

If you want to use directly DAO without a transaction just retrieve DAO implementation from data-source

PersonDaoImpl dao=dataSource.getPersonDao();
dao.insertRaw1("test", 52);

Table of Contents

Query definition

Features

Relations

Multithread supports

Modularization

Annotations for data convertion

Annotations for SQLite ORM

Annotations for shared preferences

Clone this wiki locally