com.abubusoft.kripton.android.annotation.BindDataSource Maven / Gradle / Ivy
Show all versions of kripton-orm Show documentation
/*******************************************************************************
* Copyright 2015, 2017 Francesco Benincasa ([email protected]).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.abubusoft.kripton.android.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* 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 @{@link 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
* this
* page for more information.
* - version: database version. The default version is
* 1.
* - schema: if true generates schema DDL in a separate file
* in filder
schemas
. Visit
* Kripton
* wiki for more information about it.
*
*
* 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:
*
*
*
* public class BindSampleDataSource extends AbstractDataSource implements BindSampleDaoFactory, SampleDataSource {
* static BindSampleDataSource instance;
*
* protected PersonDaoImpl personDao = new PersonDaoImpl(this);
*
* private final DataSourceSingleThread _daoFactorySingleThread = new DataSourceSingleThread();
*
* protected BindSampleDataSource(DataSourceOptions options) {
* super("feature01.db", 1, options);
* }
*
* @Override
* public PersonDaoImpl getPersonDao() {
* return personDao;
* }
*
* public void execute(Transaction transaction) {
* execute(transaction, onErrorListener);
* }
*
* 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();
* }
* }
* }
*
* public <T> T executeBatch(Batch<T> commands) {
* return executeBatch(commands, false);
* }
*
* 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;
* }
*
* public static synchronized BindSampleDataSource getInstance() {
* if (instance == null) {
* instance = new BindSampleDataSource(null);
* }
* return instance;
* }
*
* public static BindSampleDataSource open() {
* BindSampleDataSource instance = getInstance();
* instance.openWritableDatabase();
* return instance;
* }
*
* public static BindSampleDataSource openReadOnly() {
* BindSampleDataSource instance = getInstance();
* instance.openReadOnlyDatabase();
* return instance;
* }
*
* @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);
* }
* }
*
* @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);
* }
* }
*
* @Override
* public void onConfigure(SQLiteDatabase database) {
* // configure database
* if (options.databaseLifecycleHandler != null) {
* options.databaseLifecycleHandler.onConfigure(database);
* }
* }
*
* public void clearCompiledStatements() {
* PersonDaoImpl.clearCompiledStatements();
* }
*
* public static synchronized BindSampleDataSource build(DataSourceOptions options) {
* if (instance == null) {
* instance = new BindSampleDataSource(options);
* }
* instance.openWritableDatabase();
* instance.close();
* return instance;
* }
*
* public static synchronized BindSampleDataSource build() {
* return build(DataSourceOptions.builder().build());
* }
*
* public interface Transaction extends AbstractDataSource.AbstractExecutable<BindSampleDaoFactory> {
* TransactionResult onExecute(BindSampleDaoFactory daoFactory);
* }
*
* public interface Batch<T> {
* T onExecute(BindSampleDaoFactory daoFactory);
* }
*
* class DataSourceSingleThread implements BindSampleDaoFactory {
* private SQLContextSingleThreadImpl _context;
*
* private PersonDaoImpl _personDao;
*
* DataSourceSingleThread() {
* _context = new SQLContextSingleThreadImpl(BindSampleDataSource.this);
* }
*
* 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.java
* that derives from SQLiteOpenHelper.java.
*
*
*
* To use a data source in a client application, just retrieve a reference to
* singleton instance of data source
*
*
*
* BindSampleDataSource dataSource = BindSampleDataSource.getInstance();
*
*
* 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);
*
*
* @author Francesco Benincasa ([email protected])
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface BindDataSource {
/**
* DAOs to include in the database schema.
*
* @return class to include in the database schema
*/
Class>[] daoSet();
/**
* Name of database file.
*
* @return database name
*/
String fileName();
/**
* Database version.
*
* @return database version
*/
int version() default 1;
/**
* if true, generate log info.
*
* @return true if you want to produce log code
*/
boolean log() default true;
/**
*
* if true, generate schema sql file, in schema/
folder. Visit
* this page for
* more information.
*
*
*
* The name of file will be:
*
*
*
* {name of datasource}.schema.{version}.sql
*
*
* Example:
*
*
* xeno.schema.1.sql
*
*
* @return true, if successful
*/
boolean schema() default false;
/**
* if true, generate async task.
*
* @return true if you want to generate async task class
*/
boolean asyncTask() default false;
/**
* if true, generate cursor wrapper.
*
* @return true if you want to generate cursor wrapper
*/
boolean cursorWrapper() default false;
/**
* if true, generate rx support.
*
* @return true if you want to generate rx support
*/
boolean rx() default false;
/**
*
* Global sql type adapters. These adapter are applied to every property
* that adapter supports. This adapters are not applied to parameter of raw
* methods (ones that not use a bean as parameter).
*
*
*
* This adapters is ovverride by specific type adapter.
*
*
* @return the bind sql adapter[]
*/
BindSqlAdapter[] typeAdapters() default {};
}