From 08057083018ea577f024ebd89c2b296ecc95c105 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Mon, 29 Jun 2015 18:38:19 -0400 Subject: [PATCH 01/24] fixed variable names. Added test for byId method --- .../android/dbflow/test/sql/SelectTest.java | 5 + .../android/dbflow/sql/language/Where.java | 99 +++++++++---------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java index f114458a7..ba834b5c2 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java @@ -44,6 +44,11 @@ public void testSelectStatement() { .and(Condition.column(TestModel3$Table.TYPE).is("test")); assertEquals("SELECT * FROM `TestModel3` WHERE `name`='test' AND `type`='test'", where4.getQuery().trim()); + + Where where5 = new Select().from(TestModel3.class) + .byIds("Test"); + + assertEquals("SELECT * FROM `TestModel3` WHERE `name`='Test'", where5.getQuery().trim()); } public void testJoins() { diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java index c514b2667..91dfc6674 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java @@ -27,40 +27,35 @@ public class Where extends BaseModelQueriable mWhereBase; + private final WhereBase whereBase; /** * The database manager we run this query on */ - private final BaseDatabaseDefinition mManager; + private final BaseDatabaseDefinition databaseDefinition; /** * Helps to build the where statement easily */ - private ConditionQueryBuilder mConditionQueryBuilder; + private ConditionQueryBuilder conditionQueryBuilder; /** * The SQL GROUP BY method */ - private String mGroupBy; + private String groupBy; /** * The SQL HAVING */ - private ConditionQueryBuilder mHaving; + private ConditionQueryBuilder having; /** * The SQL ORDER BY */ - private String mOrderBy; + private String orderBy; /** * The SQL LIMIT */ - private String mLimit; + private String limit; /** * The SQL OFFSET */ - private String mOffset; - - /** - * Marks this WHERE as only using the EXIST keyword. - */ - private boolean isExistWhere; + private String offset; /** * Constructs this class with the specified {@link com.raizlabs.android.dbflow.config.FlowManager} @@ -70,10 +65,10 @@ public class Where extends BaseModelQueriable whereBase) { super(whereBase.getTable()); - mWhereBase = whereBase; - mManager = FlowManager.getDatabaseForTable(mWhereBase.getTable()); - mConditionQueryBuilder = new ConditionQueryBuilder<>(mWhereBase.getTable()); - mHaving = new ConditionQueryBuilder<>(mWhereBase.getTable()); + this.whereBase = whereBase; + databaseDefinition = FlowManager.getDatabaseForTable(this.whereBase.getTable()); + conditionQueryBuilder = new ConditionQueryBuilder<>(this.whereBase.getTable()); + having = new ConditionQueryBuilder<>(this.whereBase.getTable()); } /** @@ -84,7 +79,7 @@ public Where(WhereBase whereBase) { * @return */ public Where whereClause(String whereClause, Object... args) { - mConditionQueryBuilder.append(whereClause, args); + conditionQueryBuilder.append(whereClause, args); return this; } @@ -96,7 +91,7 @@ public Where whereClause(String whereClause, Object... args) { */ public Where whereQuery(ConditionQueryBuilder conditionQueryBuilder) { if (conditionQueryBuilder != null) { - mConditionQueryBuilder = conditionQueryBuilder; + this.conditionQueryBuilder = conditionQueryBuilder; } return this; } @@ -109,7 +104,7 @@ public Where whereQuery(ConditionQueryBuilder conditionQ * @return */ public Where and(String columnName, Object value) { - mConditionQueryBuilder.addCondition(columnName, value); + conditionQueryBuilder.addCondition(columnName, value); return this; } @@ -122,7 +117,7 @@ public Where and(String columnName, Object value) { * @return */ public Where and(String columnName, String operator, Object value) { - mConditionQueryBuilder.addCondition(columnName, operator, value); + conditionQueryBuilder.addCondition(columnName, operator, value); return this; } @@ -133,7 +128,7 @@ public Where and(String columnName, String operator, Object value) { * @return */ public Where and(SQLCondition condition) { - mConditionQueryBuilder.and(condition); + conditionQueryBuilder.and(condition); return this; } @@ -144,7 +139,7 @@ public Where and(SQLCondition condition) { * @return */ public Where or(SQLCondition condition) { - mConditionQueryBuilder.or(condition); + conditionQueryBuilder.or(condition); return this; } @@ -155,7 +150,7 @@ public Where or(SQLCondition condition) { * @return */ public Where andThese(List conditions) { - mConditionQueryBuilder.addConditions(conditions); + conditionQueryBuilder.addConditions(conditions); return this; } @@ -166,7 +161,7 @@ public Where andThese(List conditions) { * @return */ public Where andThese(SQLCondition... conditions) { - mConditionQueryBuilder.addConditions(conditions); + conditionQueryBuilder.addConditions(conditions); return this; } @@ -177,7 +172,7 @@ public Where andThese(SQLCondition... conditions) { * @return */ public Where groupBy(QueryBuilder groupBy) { - mGroupBy = groupBy.getQuery(); + this.groupBy = groupBy.getQuery(); return this; } @@ -188,7 +183,7 @@ public Where groupBy(QueryBuilder groupBy) { * @return */ public Where groupBy(ColumnAlias... columns) { - mGroupBy = new QueryBuilder().appendArray(columns) + groupBy = new QueryBuilder().appendArray(columns) .getQuery(); return this; } @@ -200,7 +195,7 @@ public Where groupBy(ColumnAlias... columns) { * @return */ public Where groupBy(String... columns) { - mGroupBy = new QueryBuilder().appendArray(columns) + groupBy = new QueryBuilder().appendArray(columns) .getQuery(); return this; } @@ -212,7 +207,7 @@ public Where groupBy(String... columns) { * @return */ public Where having(SQLCondition... conditions) { - mHaving.addConditions(conditions); + having.addConditions(conditions); return this; } @@ -223,7 +218,7 @@ public Where having(SQLCondition... conditions) { * @return */ public Where orderBy(boolean ascending, String... columns) { - mOrderBy = new QueryBuilder().appendArray(columns) + orderBy = new QueryBuilder().appendArray(columns) .appendSpace() .append(ascending ? "ASC" : "DESC") .getQuery(); @@ -237,7 +232,7 @@ public Where orderBy(boolean ascending, String... columns) { * @return */ public Where orderBy(String orderby) { - mOrderBy = orderby; + orderBy = orderby; return this; } @@ -248,7 +243,7 @@ public Where orderBy(String orderby) { * @return */ public Where limit(Object limit) { - mLimit = String.valueOf(limit); + this.limit = String.valueOf(limit); return this; } @@ -259,7 +254,7 @@ public Where limit(Object limit) { * @return */ public Where offset(Object offset) { - mOffset = String.valueOf(offset); + this.offset = String.valueOf(offset); return this; } @@ -269,9 +264,9 @@ public Where offset(Object offset) { * @return */ public Where exists(Where where) { - mConditionQueryBuilder.addCondition(Condition.exists() - .operation("") - .value(where)); + conditionQueryBuilder.addCondition(Condition.exists() + .operation("") + .value(where)); return this; } @@ -282,24 +277,24 @@ public Where exists(Where where) { */ public long count() { long count; - if ((mWhereBase instanceof Set) || mWhereBase.getQueryBuilderBase() instanceof Delete) { - count = SQLiteCompatibilityUtils.executeUpdateDelete(mManager.getWritableDatabase(), getQuery()); + if ((whereBase instanceof Set) || whereBase.getQueryBuilderBase() instanceof Delete) { + count = SQLiteCompatibilityUtils.executeUpdateDelete(databaseDefinition.getWritableDatabase(), getQuery()); } else { - count = DatabaseUtils.longForQuery(mManager.getWritableDatabase(), getQuery(), null); + count = DatabaseUtils.longForQuery(databaseDefinition.getWritableDatabase(), getQuery(), null); } return count; } @Override public String getQuery() { - String fromQuery = mWhereBase.getQuery(); + String fromQuery = whereBase.getQuery(); QueryBuilder queryBuilder = new QueryBuilder().append(fromQuery) - .appendQualifier("WHERE", mConditionQueryBuilder.getQuery()) - .appendQualifier("GROUP BY", mGroupBy) - .appendQualifier("HAVING", mHaving.getQuery()) - .appendQualifier("ORDER BY", mOrderBy) - .appendQualifier("LIMIT", mLimit) - .appendQualifier("OFFSET", mOffset); + .appendQualifier("WHERE", conditionQueryBuilder.getQuery()) + .appendQualifier("GROUP BY", groupBy) + .appendQualifier("HAVING", having.getQuery()) + .appendQualifier("ORDER BY", orderBy) + .appendQualifier("LIMIT", limit) + .appendQualifier("OFFSET", offset); // Don't wast time building the string // unless we're going to log it. @@ -320,11 +315,11 @@ public Cursor query() { // Query the sql here Cursor cursor = null; String query = getQuery(); - if (mWhereBase.getQueryBuilderBase() instanceof Select) { - cursor = mManager.getWritableDatabase() + if (whereBase.getQueryBuilderBase() instanceof Select) { + cursor = databaseDefinition.getWritableDatabase() .rawQuery(query, null); } else { - mManager.getWritableDatabase() + databaseDefinition.getWritableDatabase() .execSQL(query); } @@ -351,7 +346,7 @@ public List queryList() { } protected void checkSelect(String methodName) { - if (!(mWhereBase.getQueryBuilderBase() instanceof Select)) { + if (!(whereBase.getQueryBuilderBase() instanceof Select)) { throw new IllegalArgumentException("Please use " + methodName + "(). The beginning is not a Select"); } } @@ -360,7 +355,7 @@ protected void checkSelect(String methodName) { * Queries and returns only the first {@link ModelClass} result from the DB. Will enforce a limit of 1 item * returned from the database. * - * @return The first result of this query. Note: this query may return more than one from the DB. + * @return The first result of this query. Note: this query forces a limit of 1 from the database. */ @Override public ModelClass querySingle() { @@ -376,6 +371,6 @@ public ModelClass querySingle() { */ public boolean hasData() { checkSelect("query"); - return SqlUtils.hasData(mWhereBase.getTable(), getQuery()); + return SqlUtils.hasData(whereBase.getTable(), getQuery()); } } From 1514e741850b465260d467fa576f32c7af228551 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Mon, 29 Jun 2015 18:47:34 -0400 Subject: [PATCH 02/24] lazy load the associated adapters for more lax model inheritance. --- .../android/dbflow/structure/BaseModel.java | 27 ++++++++++--------- .../dbflow/structure/BaseModelView.java | 17 +++++++----- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java index 276555dc9..bb0f8d3ea 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java @@ -42,44 +42,39 @@ public enum Action { CHANGE } - private ModelAdapter mModelAdapter; - - public BaseModel() { - mModelAdapter = FlowManager.getModelAdapter(getClass()); - } + private ModelAdapter modelAdapter; @SuppressWarnings("unchecked") @Override public void save() { - mModelAdapter.save(this); + getModelAdapter().save(this); } @SuppressWarnings("unchecked") @Override public void delete() { - mModelAdapter.delete(this); + getModelAdapter().delete(this); } @SuppressWarnings("unchecked") @Override public void update() { - mModelAdapter.update(this); + getModelAdapter().update(this); } /** * Directly tries to insert this item into the DB without updating. - * */ @SuppressWarnings("unchecked") @Override public void insert() { - mModelAdapter.insert(this); + getModelAdapter().insert(this); } @SuppressWarnings("unchecked") @Override public boolean exists() { - return mModelAdapter.exists(this); + return getModelAdapter().exists(this); } /** @@ -89,7 +84,15 @@ public AsyncModel async() { return new AsyncModel<>(this); } + /** + * @return The associated {@link ModelAdapter}. The {@link FlowManager} + * may throw a {@link InvalidDBConfiguration} for this call if this class + * is not associated with a table, so be careful when using this method. + */ public ModelAdapter getModelAdapter() { - return mModelAdapter; + if (modelAdapter == null) { + modelAdapter = FlowManager.getModelAdapter(getClass()); + } + return modelAdapter; } } diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModelView.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModelView.java index 25a19fe8e..9947275d3 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModelView.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModelView.java @@ -10,15 +10,18 @@ public abstract class BaseModelView extends BaseFinalM private ModelViewAdapter> adapter; - @SuppressWarnings("unchecked") - public BaseModelView() { - adapter = ((ModelViewAdapter>) FlowManager.getModelViewAdapter( - getClass())); - } - @Override public boolean exists() { - return adapter.exists(this); + return getModelViewAdapter().exists(this); + } + + @SuppressWarnings("unchecked") + public ModelViewAdapter> getModelViewAdapter() { + if (adapter == null) { + adapter = ((ModelViewAdapter>) + FlowManager.getModelViewAdapter(getClass())); + } + return adapter; } } From 9e5d752c4b918711e35c8404393fc99be0d7e37d Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Mon, 29 Jun 2015 20:53:29 -0400 Subject: [PATCH 03/24] adding column value validation to prevent against SQL injection. --- .../annotation/ColumnValueValidator.java | 41 +++++++++++++++++++ .../android/dbflow/sql/language/Where.java | 2 +- .../sql/validation/ColumnValueValidator.java | 14 +++++++ .../InvalidColumnValueException.java | 11 +++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java create mode 100644 DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java create mode 100644 DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java diff --git a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java new file mode 100644 index 000000000..e2e0a4678 --- /dev/null +++ b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java @@ -0,0 +1,41 @@ +package com.raizlabs.android.dbflow.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Description: Registers a ColumnValueValidator with DBFlow to be applied to + * a specific column from a specific table. + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface ColumnValueValidator { + + /** + * If set (default), if an invalid value is passed, we will throw an exception. + */ + int HANDLING_CRASH = -1; + + /** + * If set, the default value for a column is used. + */ + int HANDLING_USE_DEFAULT = 0; + + /** + * If set, you can specify a replacement string to be used in a more graceful fallback. + */ + int HANDLING_SPECIFY_STRING = 1; + + /** + * @return {@link #HANDLING_CRASH} by default. Specify how the column value falls back. + */ + int handling() default HANDLING_CRASH; + + /** + * @return The value you wish to use to fallback in the case the {@link #handling()} is {@link #HANDLING_SPECIFY_STRING} + * and the ColumnValueValidator fails. + */ + String stringFallback() default ""; +} diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java index 91dfc6674..3672e06ba 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java @@ -218,7 +218,7 @@ public Where having(SQLCondition... conditions) { * @return */ public Where orderBy(boolean ascending, String... columns) { - orderBy = new QueryBuilder().appendArray(columns) + orderBy = new QueryBuilder().appendQuotedArray(columns) .appendSpace() .append(ascending ? "ASC" : "DESC") .getQuery(); diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java new file mode 100644 index 000000000..405b8e74d --- /dev/null +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java @@ -0,0 +1,14 @@ +package com.raizlabs.android.dbflow.sql.validation; + +/** + * Description: This class's sole purpose is to validate column values and + * react to outliers or invalid data via SQL injection. + */ +public abstract class ColumnValueValidator { + + /** + * @return true if the specified value is a valid value, false if not. + */ + public abstract boolean isValid(Object value); + +} diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java new file mode 100644 index 000000000..fae6b7d67 --- /dev/null +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java @@ -0,0 +1,11 @@ +package com.raizlabs.android.dbflow.sql.validation; + +/** + * Description: Thrown when an invalid column value is passed in a {@link ColumnValueValidator}. + */ +public class InvalidColumnValueException extends RuntimeException { + + public InvalidColumnValueException(String detailMessage) { + super(detailMessage); + } +} From d81d0d079ea7a98199535acd8422f91479658139 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Mon, 29 Jun 2015 22:48:14 -0400 Subject: [PATCH 04/24] adding in support for more specific updates to models in tables. --- .../dbflow/runtime/FlowContentObserver.java | 77 ++++++++++++++++--- .../raizlabs/android/dbflow/sql/SqlUtils.java | 56 ++++++++++---- .../dbflow/structure/InternalAdapter.java | 2 - .../dbflow/structure/ModelAdapter.java | 2 +- .../container/ModelContainerAdapter.java | 2 +- 5 files changed, 107 insertions(+), 32 deletions(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java index 0f9214291..83d24ebac 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java @@ -14,8 +14,10 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Description: Listens for {@link com.raizlabs.android.dbflow.structure.Model} changes. Register for specific @@ -47,8 +49,9 @@ public static void setShouldForceNotify(boolean forceNotify) { } /** - * Provides default implementation of the {@link FlowContentObserver.OnModelStateChangedListener} - * enabling you to only have to implement a small subset of methods. + * Called when the {@link FlowContentObserver} receives a URI from a model change event. This event + * is generalized to not include specific model keys. To get more specific events, register a + * {@link OnSpecificModelStateChangedListener}. */ public interface OnModelStateChangedListener { @@ -64,14 +67,29 @@ public interface OnModelStateChangedListener { void onModelStateChanged(Class table, BaseModel.Action action); } - /** - * Listeners for model changes. - */ + public interface OnSpecificModelStateChangedListener { + + /** + * Notifies that the state of a {@link com.raizlabs.android.dbflow.structure.Model} + * has changed for the table this is registered for. + * + * @param table The table that this change occurred on. This is ONLY available on {@link Build.VERSION_CODES#JELLY_BEAN} + * and up. + * @param action The action on the model. for versions prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN} , + * the {@link com.raizlabs.android.dbflow.structure.BaseModel.Action#CHANGE} will always be called for any action. + * @param columnName The name of the primary column with the id that's changed. + * @param value the value from the primary key thats changed converted to String. + */ + void onModelStateChanged(Class table, BaseModel.Action action, String columnName, String value); + } + private final List modelChangeListeners = new ArrayList<>(); + private final List specificModelChangeListeners = new ArrayList<>(); + private final Map> registeredTables = new HashMap<>(); - private final List notificationUris = new ArrayList<>(); + private final Set notificationUris = new HashSet<>(); protected boolean isInTransaction = false; private boolean notifyAllUris = false; @@ -143,6 +161,24 @@ public void removeModelChangeListener(OnModelStateChangedListener modelChangeLis modelChangeListeners.remove(modelChangeListener); } + /** + * Add a specific listener for model changes + * + * @param modelChangeListener + */ + public void addSpecificModelChangeListener(OnSpecificModelStateChangedListener modelChangeListener) { + specificModelChangeListeners.add(modelChangeListener); + } + + /** + * Removes a specific listener for model changes + * + * @param modelChangeListener + */ + public void removeSpecificModelChangeListener(OnSpecificModelStateChangedListener modelChangeListener) { + specificModelChangeListeners.remove(modelChangeListener); + } + /** * Registers the observer for model change events for specific class. */ @@ -176,7 +212,22 @@ public void onChange(boolean selfChange) { @Override public void onChange(boolean selfChange, Uri uri) { String fragment = uri.getFragment(); - String tableName = uri.getEncodedSchemeSpecificPart().replace("//", ""); + String tableName = uri.getAuthority(); + + String columnName = null; + String param = null; + + Set queryNames = uri.getQueryParameterNames(); + if (!queryNames.isEmpty()) { + for (String key : queryNames) { + // for now we get first key we find + // maybe in future we add multi-column support + param = uri.getQueryParameter(key); + columnName = key; + break; + } + } + Class table = registeredTables.get(tableName); if (!isInTransaction) { @@ -185,6 +236,12 @@ public void onChange(boolean selfChange, Uri uri) { for (OnModelStateChangedListener modelChangeListener : modelChangeListeners) { modelChangeListener.onModelStateChanged(table, action); } + + if (columnName != null && param != null) { + for (OnSpecificModelStateChangedListener modelChangeListener : specificModelChangeListeners) { + modelChangeListener.onModelStateChanged(table, action, columnName, param); + } + } } } else { // convert this uri to a CHANGE op if we don't care about individual changes. @@ -192,10 +249,8 @@ public void onChange(boolean selfChange, Uri uri) { uri = SqlUtils.getNotificationUri(table, BaseModel.Action.CHANGE); } synchronized (notificationUris) { - if (!notificationUris.contains(uri)) { - // add and keep track of unique notification uris for when transaction completes. - notificationUris.add(uri); - } + // add and keep track of unique notification uris for when transaction completes. + notificationUris.add(uri); } } } diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java index 9a13e2d74..5167a5c6d 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java @@ -185,7 +185,7 @@ public static ModelClass querySingle(Class) modelClass, - cursor); + cursor); } else { retModel = convertToModel(false, modelClass, cursor); } @@ -235,7 +235,8 @@ void save(TableClass model, AdapterClass adapter, ModelAdapter model } if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.SAVE); + notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.SAVE, modelAdapter.getCachingColumnName(), + adapter.getCachingId(model)); } } @@ -255,15 +256,16 @@ boolean update(TableClass model, AdapterClass adapter, ModelAdapter ContentValues contentValues = new ContentValues(); adapter.bindToContentValues(contentValues, model); exists = (SQLiteCompatibilityUtils.updateWithOnConflict(db, modelAdapter.getTableName(), contentValues, - adapter.getPrimaryModelWhere(model).getQuery(), null, - ConflictAction.getSQLiteDatabaseAlgorithmInt( - modelAdapter.getUpdateOnConflictAction())) != - 0); + adapter.getPrimaryModelWhere(model).getQuery(), null, + ConflictAction.getSQLiteDatabaseAlgorithmInt( + modelAdapter.getUpdateOnConflictAction())) != + 0); if (!exists) { // insert insert(model, adapter, modelAdapter); } else if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.UPDATE); + notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.UPDATE, + modelAdapter.getCachingColumnName(), adapter.getCachingId(model)); } return exists; } @@ -282,7 +284,8 @@ void insert(TableClass model, AdapterClass adapter, ModelAdapter mod long id = insertStatement.executeInsert(); adapter.updateAutoIncrement(model, id); if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.INSERT); + notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.INSERT, + modelAdapter.getCachingColumnName(), adapter.getCachingId(model)); } } @@ -293,13 +296,14 @@ void insert(TableClass model, AdapterClass adapter, ModelAdapter mod * @param model The model to delete */ @SuppressWarnings("unchecked") - public static - void delete(final TableClass model, AdapterClass modelAdapter) { - new Delete().from((Class) modelAdapter.getModelClass()).where( - modelAdapter.getPrimaryModelWhere(model)).query(); - modelAdapter.updateAutoIncrement(model, 0); + public static + void delete(final TableClass model, AdapterClass adapter, ModelAdapter modelAdapter) { + new Delete().from((Class) adapter.getModelClass()).where( + adapter.getPrimaryModelWhere(model)).query(); + adapter.updateAutoIncrement(model, 0); if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.DELETE); + notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.DELETE, + modelAdapter.getCachingColumnName(), adapter.getCachingId(model)); } } @@ -309,8 +313,8 @@ void delete(final TableClass model, AdapterClass modelAdapter) { * @param action The {@link com.raizlabs.android.dbflow.structure.BaseModel.Action} enum * @param table The table of the model */ - public static void notifyModelChanged(Class table, BaseModel.Action action) { - FlowManager.getContext().getContentResolver().notifyChange(getNotificationUri(table, action), null, true); + public static void notifyModelChanged(Class table, BaseModel.Action action, String notifyKey, Object notifyValue) { + FlowManager.getContext().getContentResolver().notifyChange(getNotificationUri(table, action, notifyKey, notifyValue), null, true); } /** @@ -319,14 +323,32 @@ public static void notifyModelChanged(Class table, BaseModel.Ac * @param modelClass * @return */ - public static Uri getNotificationUri(Class modelClass, BaseModel.Action action) { + public static Uri getNotificationUri(Class modelClass, BaseModel.Action action, String notifyKey, Object notifyValue) { String mode = ""; if (action != null) { mode = "#" + action.name(); } + Uri.Builder uriBuilder = new Uri.Builder().scheme("dbflow") + .authority(FlowManager.getTableName(modelClass)); + if (action != null) { + uriBuilder.fragment(action.name()); + } + if (notifyKey != null) { + uriBuilder.appendQueryParameter(Uri.encode(notifyKey), Uri.encode(String.valueOf(notifyValue))); + } return Uri.parse("dbflow://" + FlowManager.getTableName(modelClass) + mode); } + /** + * @param modelClass The model class to use. + * @param action The {@link BaseModel.Action} to use. + * @return The uri for updates to {@link Model}, meant for general changes. + */ + public static Uri getNotificationUri(Class modelClass, BaseModel.Action action) { + return getNotificationUri(modelClass, action, null, null); + } + + /** * Drops an active TRIGGER by specifying the onTable and triggerName * diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java index 941438906..112b79540 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java @@ -6,8 +6,6 @@ import com.raizlabs.android.dbflow.annotation.PrimaryKey; import com.raizlabs.android.dbflow.structure.container.ModelContainerAdapter; -import java.util.Objects; - /** * Description: Used for our internal Adapter classes such as generated {@link com.raizlabs.android.dbflow.structure.ModelAdapter} * or {@link ModelContainerAdapter} diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java index b04358688..6e0814504 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java @@ -81,7 +81,7 @@ public void update(ModelClass model) { */ @Override public void delete(ModelClass model) { - SqlUtils.delete(model, this); + SqlUtils.delete(model, this, this); } /** diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/container/ModelContainerAdapter.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/container/ModelContainerAdapter.java index e5a228c34..56736fd6c 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/container/ModelContainerAdapter.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/container/ModelContainerAdapter.java @@ -46,7 +46,7 @@ public void update(ModelContainer modelContainer) { */ @Override public void delete(ModelContainer modelContainer) { - SqlUtils.delete(modelContainer, this); + SqlUtils.delete(modelContainer, this, modelContainer.getModelAdapter()); } /** From 200ea5cb516a6efa989820f33f9bed48b71a8c3d Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Mon, 29 Jun 2015 23:10:04 -0400 Subject: [PATCH 05/24] fixed double quote issue with byIds() method --- .../contentobserver/ContentObserverTest.java | 12 ++++++++++++ .../android/dbflow/test/sql/TestModel3.java | 2 -- .../dbflow/runtime/FlowContentObserver.java | 2 +- .../raizlabs/android/dbflow/sql/SqlUtils.java | 6 +----- .../sql/builder/ConditionQueryBuilder.java | 16 ++++++++++------ .../android/dbflow/sql/builder/SQLCondition.java | 5 +++++ 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/contentobserver/ContentObserverTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/contentobserver/ContentObserverTest.java index 88372895e..b17cf1f3d 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/contentobserver/ContentObserverTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/contentobserver/ContentObserverTest.java @@ -1,17 +1,29 @@ package com.raizlabs.android.dbflow.test.contentobserver; +import android.net.Uri; + import com.raizlabs.android.dbflow.runtime.FlowContentObserver; +import com.raizlabs.android.dbflow.sql.SqlUtils; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.Model; import com.raizlabs.android.dbflow.test.FlowTestCase; import com.raizlabs.android.dbflow.test.structure.TestModel1; +import com.raizlabs.android.dbflow.test.structure.TestModel1$Table; /** * Description: */ public class ContentObserverTest extends FlowTestCase { + public void testNotificationUri() { + + Uri notificationUri = SqlUtils.getNotificationUri(TestModel1.class, BaseModel.Action.SAVE, TestModel1$Table.NAME, "this is a %test"); + assertEquals(notificationUri.getAuthority(), TestModel1$Table.TABLE_NAME); + assertEquals(notificationUri.getFragment(), BaseModel.Action.SAVE.name()); + assertEquals(Uri.decode(notificationUri.getQueryParameter(TestModel1$Table.NAME)), "this is a %test"); + } + public void testContentObserver() { Delete.table(TestModel1.class); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TestModel3.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TestModel3.java index 1aeaa8764..cd3804252 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TestModel3.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TestModel3.java @@ -7,8 +7,6 @@ import com.raizlabs.android.dbflow.test.structure.TestModel1; /** -* Author: andrewgrosner -* Contributors: { } * Description: */ @Table(databaseName = TestDatabase.NAME) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java index 83d24ebac..3d4987bce 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java @@ -222,7 +222,7 @@ public void onChange(boolean selfChange, Uri uri) { for (String key : queryNames) { // for now we get first key we find // maybe in future we add multi-column support - param = uri.getQueryParameter(key); + param = Uri.decode(uri.getQueryParameter(key)); columnName = key; break; } diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java index 5167a5c6d..154354dea 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java @@ -324,10 +324,6 @@ public static void notifyModelChanged(Class table, BaseModel.Ac * @return */ public static Uri getNotificationUri(Class modelClass, BaseModel.Action action, String notifyKey, Object notifyValue) { - String mode = ""; - if (action != null) { - mode = "#" + action.name(); - } Uri.Builder uriBuilder = new Uri.Builder().scheme("dbflow") .authority(FlowManager.getTableName(modelClass)); if (action != null) { @@ -336,7 +332,7 @@ public static Uri getNotificationUri(Class modelClass, BaseMode if (notifyKey != null) { uriBuilder.appendQueryParameter(Uri.encode(notifyKey), Uri.encode(String.valueOf(notifyValue))); } - return Uri.parse("dbflow://" + FlowManager.getTableName(modelClass) + mode); + return uriBuilder.build(); } /** diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/ConditionQueryBuilder.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/ConditionQueryBuilder.java index fe0df309c..ef83e855c 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/ConditionQueryBuilder.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/ConditionQueryBuilder.java @@ -13,6 +13,8 @@ import java.util.ArrayList; import java.util.List; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: Constructs a condition statement for a specific {@link com.raizlabs.android.dbflow.structure.Model} class. * This enables easy combining of conditions for SQL statements and will handle converting the model value for each column into @@ -123,7 +125,7 @@ public String getQuery() { if (isChanged || query.length() == 0) { isChanged = false; query = new StringBuilder(); - if(whereRaw != null) { + if (whereRaw != null) { query.append(whereRaw); } @@ -189,7 +191,7 @@ public ConditionQueryBuilder appendArgumentList(List arguments) { * @return This builder. */ public ConditionQueryBuilder append(String selection, Object... selectionArgs) { - if(selection != null) { + if (selection != null) { String toAppend = selection; if (selectionArgs != null) { @@ -260,9 +262,9 @@ public String convertValueToString(Object value) { if (value instanceof Number) { stringVal = String.valueOf(value); } else { - if(value instanceof Where) { + if (value instanceof Where) { stringVal = String.format("(%1s)", ((Where) value).getQuery().trim()); - } else if(value instanceof ColumnAlias) { + } else if (value instanceof ColumnAlias) { stringVal = ((ColumnAlias) value).getQuery(); } else { stringVal = String.valueOf(value); @@ -347,7 +349,7 @@ public ConditionQueryBuilder addCondition(String columnName, String throw new IllegalStateException("The " + ConditionQueryBuilder.class.getSimpleName() + " is " + "operating in empty param mode. All params must be empty"); } - return addCondition(Condition.column(columnName).operation(operator).value(value)); + return addCondition(column(columnName).operation(operator).value(value)); } @@ -450,7 +452,9 @@ public ConditionQueryBuilder replaceEmptyParams(Object... values) { ConditionQueryBuilder conditionQueryBuilder = new ConditionQueryBuilder<>(modelAdapter.getModelClass()); for (int i = 0; i < values.length; i++) { - conditionQueryBuilder.addCondition(conditions.get(i).columnName(), values[i]); + SQLCondition condition = conditions.get(i); + conditionQueryBuilder.addCondition(column(ColumnAlias.columnRaw(condition.columnName())) + .operation(condition.operation()).value(values[i])); } return conditionQueryBuilder; diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/SQLCondition.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/SQLCondition.java index c60a152cb..f4a330aaf 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/SQLCondition.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/builder/SQLCondition.java @@ -50,6 +50,11 @@ public interface SQLCondition { */ boolean hasSeparator(); + /** + * @return the operation that is used. + */ + String operation(); + /** * @return The raw value of the condition. */ From cfc2afcc8968273f1cda7c01fe119be4a2b2da4e Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 08:55:07 -0400 Subject: [PATCH 06/24] added the OrderBy object to aid in building the query piece. updated readme and version to 2.2.0. --- .../android/dbflow/sql/QueryBuilder.java | 9 +++ .../dbflow/sql/language/ColumnAlias.java | 7 ++ .../android/dbflow/sql/language/From.java | 41 +++++++--- .../android/dbflow/sql/language/OrderBy.java | 80 +++++++++++++++++++ .../android/dbflow/sql/language/Where.java | 40 +++++----- README.md | 11 ++- gradle.properties | 2 +- 7 files changed, 157 insertions(+), 33 deletions(-) create mode 100644 DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java diff --git a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java index 1b6bf6f98..75fecb10c 100644 --- a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java +++ b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java @@ -212,6 +212,15 @@ public String getQuery() { return query.toString(); } + /** + * @param columnName The column name to use. + * @return A name in quotes. E.G. index => `index` so we can use keywords as column names without fear + * of clashing. + */ + public static String quote(String columnName) { + return "`" + columnName.replace(".", "`.`") + "`"; + } + /** * Returns a string containing the tokens joined by delimiters. * diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java index b0c838da3..3aa42d305 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java @@ -102,6 +102,13 @@ public String getQuery() { return queryBuilder.getQuery(); } + /** + * @return The "AS" name from this column (quoted). If not specified, it will default to the original column name. + */ + public String getAliasName() { + return QueryBuilder.quote(!TextUtils.isEmpty(asName) ? asName : columnName); + } + @Override public String toString() { return getQuery(); diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java index 46f37b7b9..f5feb454f 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java @@ -106,29 +106,48 @@ public Where where() { } /** - * Returns a {@link Where} statement with the specified {@link com.raizlabs.android.dbflow.sql.builder.ConditionQueryBuilder} - * * @param conditionQueryBuilder The builder of a specific set of conditions used in this query - * @return A where statement. + * @return A {@link Where} statement with the specified {@link com.raizlabs.android.dbflow.sql.builder.ConditionQueryBuilder}. */ public Where where(ConditionQueryBuilder conditionQueryBuilder) { return where().whereQuery(conditionQueryBuilder); } /** - * Returns a {@link Where} statement with the specified array of {@link com.raizlabs.android.dbflow.sql.builder.Condition} - * * @param conditions The array of conditions that define this WHERE statement - * @return A where statement. + * @return A {@link Where} statement with the specified array of {@link com.raizlabs.android.dbflow.sql.builder.Condition}. */ public Where where(SQLCondition... conditions) { return where().andThese(conditions); } /** - * Run this query and returns the {@link android.database.Cursor} for it - * - * @return the Sqlite {@link android.database.Cursor} from this query + * @param orderBy The string order by to use. + * @return A {@link Where} with the specified ORDER BY string. + */ + public Where orderBy(String orderBy) { + return where().orderBy(orderBy); + } + + /** + * @param orderBy The {@link OrderBy} to use. + * @return A {@link Where} with the specified {@link OrderBy}. + */ + public Where orderBy(OrderBy orderBy) { + return where().orderBy(orderBy); + } + + /** + * @param ascending If we should be in ascending order + * @param columns The columns to specify. + * @return This WHERE query. + */ + public Where orderBy(boolean ascending, String... columns) { + return where().orderBy(ascending, columns); + } + + /** + * @return the result of the query as a {@link Cursor}. */ @Override public Cursor query() { @@ -146,9 +165,7 @@ public List queryList() { } /** - * Queries and returns only the first {@link ModelClass} result from the DB. - * - * @return The first result of this query. Note: this query may return more than one from the DB. + * @return The first result of this query. It forces a {@link Where#limit(Object)} of 1 for more efficient querying. */ @Override public ModelClass querySingle() { diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java new file mode 100644 index 000000000..b8ff8663d --- /dev/null +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java @@ -0,0 +1,80 @@ +package com.raizlabs.android.dbflow.sql.language; + +import com.raizlabs.android.dbflow.sql.Query; +import com.raizlabs.android.dbflow.sql.QueryBuilder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Description: Class that represents a SQL order-by. + */ +public class OrderBy implements Query { + + private List columnAliasList = new ArrayList<>(); + + private boolean ascending = false; + + private OrderBy() { + + } + + /** + * @param columnAliases The varg of aliases used in this {@link OrderBy} query. + * @return A new instance with the specified alias' + */ + public static OrderBy columns(ColumnAlias... columnAliases) { + OrderBy orderBy = new OrderBy(); + orderBy.columnAliasList.addAll(Arrays.asList(columnAliases)); + return orderBy; + } + + /** + * @param columnNames The varg of column names used in this {@link OrderBy} query. + * @return A new instance with the specific column names. + */ + public static OrderBy columns(String... columnNames) { + OrderBy orderBy = new OrderBy(); + for (String column : columnNames) { + orderBy.columnAliasList.add(ColumnAlias.column(column)); + } + return orderBy; + } + + /** + * @return Orders the results in ascending order. + */ + public OrderBy ascending() { + return setAscending(true); + } + + /** + * @return Orders the results in descending order. + */ + public OrderBy descending() { + return setAscending(false); + } + + /** + * @param isAscending if the ORDER BY is ascending. + * @return This instance if its ascending or descending. + */ + public OrderBy setAscending(boolean isAscending) { + ascending = isAscending; + return this; + } + + @Override + public String getQuery() { + QueryBuilder queryBuilder = new QueryBuilder("ORDER BY "); + for (int i = 0; i < columnAliasList.size(); i++) { + if (i > 0) { + queryBuilder.append(", "); + } + queryBuilder.append(columnAliasList.get(i).getAliasName()); + } + queryBuilder.appendSpace().append(ascending ? "ASC" : "DESC"); + return queryBuilder.getQuery(); + } +} diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java index 3672e06ba..adad26d43 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java @@ -212,24 +212,19 @@ public Where having(SQLCondition... conditions) { } /** - * Defines a SQL ORDER BY statement without the ORDER BY. - * * @param ascending If we should be in ascending order - * @return + * @param columns the columns to specify. + * @return This WHERE query. */ public Where orderBy(boolean ascending, String... columns) { - orderBy = new QueryBuilder().appendQuotedArray(columns) - .appendSpace() - .append(ascending ? "ASC" : "DESC") + orderBy = OrderBy.columns(columns).setAscending(ascending) .getQuery(); return this; } /** - * Defines a SQL ORDER BY statement without the ORDER BY. - * - * @param orderby The orderBy command - * @return + * @param orderby The orderBy string that we use. + * @return This WHERE query. */ public Where orderBy(String orderby) { orderBy = orderby; @@ -237,10 +232,19 @@ public Where orderBy(String orderby) { } /** - * Defines a SQL LIMIT statement without the LIMIT. + * @param orderby The {@link OrderBy} + * @return This WHERE query. + */ + public Where orderBy(OrderBy orderby) { + orderBy = orderby.getQuery(); + return this; + } + + /** + * Specify the limit value you wish to use.. * - * @param limit - * @return + * @param limit The limit. E.g. 1 + * @return This WHERE query. */ public Where limit(Object limit) { this.limit = String.valueOf(limit); @@ -248,10 +252,10 @@ public Where limit(Object limit) { } /** - * Defines a SQL OFFSET statement without the OFFSET. + * Add an OFFSET value to this query. * - * @param offset - * @return + * @param offset The offset value. + * @return This WHERE query. */ public Where offset(Object offset) { this.offset = String.valueOf(offset); @@ -306,9 +310,7 @@ public String getQuery() { } /** - * Run this query and returns the {@link android.database.Cursor} for it - * - * @return the Sqlite {@link android.database.Cursor} from this query + * @return the result of the query as a {@link Cursor}. */ @Override public Cursor query() { diff --git a/README.md b/README.md index 2d790ed4d..421549742 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Image](https://github.com/agrosner/DBFlow/blob/develop/dbflow_banner.png?raw=true) -[![JCenter](https://img.shields.io/badge/JCenter-2.1.0-red.svg?style=flat)](https://bintray.com/raizlabs/Libraries/DBFlow/view) +[![JCenter](https://img.shields.io/badge/JCenter-2.2.0-red.svg?style=flat)](https://bintray.com/raizlabs/Libraries/DBFlow/view) [![Android Weekly](http://img.shields.io/badge/Android%20Weekly-%23129-2CB3E5.svg?style=flat)](http://androidweekly.net/issues/issue-129) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-DBFlow-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1134) @@ -33,6 +33,15 @@ If you wish to have your application featured here, please file an [issue](https ## Changelog +#### 2.2.0 + +1. Fixed a bug where `new Select().from(myTable.class).byId(PrimaryKey)` was incorrectly double-quoting columns. +2. Adds a primary key into the URI of a `FlowContentObserver` for single primary key tables. +3. Lazy loads `ModelAdapter` and `ModelViewAdapter` so subclassing a non-table `BaseModel` now +works without crashing/complaining. Just don't call the non-tables associated `Model` methods directly. +4. Bug fixes and Improvements +5. Adds ability to validate values for columns via the [ColumnValueValidator](https://github.com/Raizlabs/DBFlow/blob/master/usage/Conditions.md). + #### 2.1.0 1. Library now is on jCenter()/bintray! diff --git a/gradle.properties b/gradle.properties index 621253b33..c41e2078f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=2.1.0 +version=2.2.0 version_code=1 group=com.raizlabs.android From 039bf60f40e6f47f76d888b60d9c1aabdf7deea6 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 10:02:14 -0400 Subject: [PATCH 07/24] souped up the documentation on FlowQueryList and FlowTableList. Added a new method to ease the enabling of self-refreshes when models change. --- .../android/dbflow/list/FlowQueryList.java | 20 +++ README.md | 7 +- usage/TableList.md | 117 ++++++++++++++++-- 3 files changed, 132 insertions(+), 12 deletions(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/list/FlowQueryList.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/list/FlowQueryList.java index 4d1ed110e..8caaa07f9 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/list/FlowQueryList.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/list/FlowQueryList.java @@ -22,6 +22,7 @@ import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; +import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.Model; import com.raizlabs.android.dbflow.structure.cache.ModelCache; import com.raizlabs.android.dbflow.structure.cache.ModelLruCache; @@ -189,6 +190,25 @@ public void refresh() { internalCursorList.refresh(); } + /** + * Registers itself for content changes on the specific table that this list is for. When + * any model data is changed via the {@link Model} methods, we call {@link #refresh()} on this underlying data. + * To prevent many refreshes, call {@link #beginTransaction()} before making changes to a set of models, + * and then when finished call {@link #endTransactionAndNotify()}. + */ + public void enableSelfRefreshes(Context context) { + registerForContentChanges(context); + addModelChangeListener(new OnModelStateChangedListener() { + @Override + public void onModelStateChanged(Class table, BaseModel.Action action) { + if (internalCursorList.getTable().equals(table)) { + refresh(); + } + } + }); + } + + /** * Adds an item to this table, but does not allow positonal insertion. Same as calling * {@link #add(com.raizlabs.android.dbflow.structure.Model)} diff --git a/README.md b/README.md index 421549742..40e6a2933 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,10 @@ If you wish to have your application featured here, please file an [issue](https 3. Lazy loads `ModelAdapter` and `ModelViewAdapter` so subclassing a non-table `BaseModel` now works without crashing/complaining. Just don't call the non-tables associated `Model` methods directly. 4. Bug fixes and Improvements -5. Adds ability to validate values for columns via the [ColumnValueValidator](https://github.com/Raizlabs/DBFlow/blob/master/usage/Conditions.md). +5. Adds ability to validate values for columns via the [ColumnValueValidator](https://github.com/Raizlabs/DBFlow/blob/master/usage/Conditions.md). +6. Adds the `OrderBy` object to aid in `ORDER BY` queries. Added `orderBy()` methods in the `From` class +for easier access without needing to call `where()` first. + #### 2.1.0 @@ -86,7 +89,7 @@ For more detailed usage, check out these sections: [Observing Models](https://github.com/Raizlabs/DBFlow/blob/master/usage/ObservableModels.md) -[Tables as Lists](https://github.com/Raizlabs/DBFlow/blob/master/usage/TableList.md) +[Queries as Lists](https://github.com/Raizlabs/DBFlow/blob/master/usage/TableList.md) [Triggers, Indexes, and More](https://github.com/Raizlabs/DBFlow/blob/master/usage/TriggersIndexesAndMore.md) diff --git a/usage/TableList.md b/usage/TableList.md index 79cef4ee9..cded502ae 100644 --- a/usage/TableList.md +++ b/usage/TableList.md @@ -13,6 +13,33 @@ This class becomes useful when we have a very large dataset that we wish to disp keep memory usage to a slim minimum by only loading items we need. This class is similar to `CursorAdapter` except it does not extend any adapter class and provides a simple yet effective API for retrieving and converted data from the database with ease. +There are a few ways of defining it: + 1. Using any valid `ModelQueriable` such as `From`, `StringQuery`, and `Where` + + ```java + // true to cache models, false to not. + new FlowCursorList<>(true, new Select().from(TestModel.class) + .where(Condition.column(TestModel1$Table.NAME).like("pasta%"))); + + // true to cache models, specifying a size of the cache (ignored if the cache does not support it) + new FlowCursorList<>(1000, new Select().from(TestModel.class) + .where(Condition.column(TestModel1$Table.NAME).like("pasta%"))); + ``` + + 2. Same constructors but specify table and Conditions: + + ```java + + new FlowCursorList<>(true, TestModel.class, + Condition.column(TestModel1$Table.NAME).like("pasta%")); + + new FlowCursorList<>(1000, TestModel.class, + Condition.column(TestModel1$Table.NAME).like("pasta%")); + + ``` + +### Example + ```java private class TestModelAdapter extends BaseAdapter { @@ -48,27 +75,97 @@ not extend any adapter class and provides a simple yet effective API for retriev ``` -## Table List +### Specifying custom caches + +To use a different cache than the default `LruCache`, override `getBackingCache()`: + +```java + +FlowCursorList list = new FlowCursorList(true, someWhere) { + + @Override + protected ModelCache getBackingCache() { + return new SparseArrayBasedCache(); + } + +} + +``` + +## Query List -A `FlowQueryList` is java `List` implementation of managing a database table. -All modifications affect the table in real-time. -All modifications, by default, are immediate. -If you wish to not run these on the main thread, call `flowTableList.setTransact(true)`. +A `FlowQueryList` is : + 1. Java `List` implementation of managing a database table. + 2. All modifications affect the table in real-time. + 3. All modifications, by default, are immediate. + 4. If you wish to not run these on the main thread, call `flowTableList.setTransact(true)`. Internally its backed by a `FlowCursorList` to include all of it's existing functionality. +### Best Practices + + 1. Avoid multiple single operations in a loop and instead use the batch methods. Each operation calls the internal `refresh()`, + which reruns the query in the DB. + + ```java + + FlowQueryList flowQueryList = ...; + + // DONT + for (int i = 0; i < 50; i++) { + TableModel object = anotherList.get(i); + flowQueryList.remove(object); + } + + // DO + flowQueryList.removeAll(anotherList); + + ``` + + 2. When making changes outside of the `FlowQueryList`, the data contained becomes stale. + You can either: A. call `refresh()` before querying its data again or B. Register + self-refreshes: + + ```java + + flowQueryList.enableSelfRefreshes(context); + + ``` + + 3. If you're making tons of single changes (using the FlowQueryList or not), it's much more efficient to use a transaction. In this instance we delay the notification of listeners until the + + ```java + + flowQueryList.beginTransaction(); + + // perform model changes!! + + // calls any listeners associated with it (including the listener we registered earlier) + // refreshes the list here (if registered) + flowQueryList.endTransactionAndNotify(); + + ``` + + +### Example + ```java -FlowTableList flowTableList = new FlowTableList(TestModel1.class); +FlowQueryList flowQueryList = new FlowQueryList(TestModel1.class); + +flowQueryList.beginTransaction(); // Deletes from the table and returns the item -TestModel1 model1 = flowTableList.remove(0); +TestModel1 model1 = flowQueryList.remove(0); // Saves the item back into the DB, updates if it already exists -flowTableList.add(model1); +flowQueryList.add(model1); // Updates the item in the DB -flowTableList.set(model1); +flowQueryList.set(model1); // Clears the whole table -flowTableList.clear(); +flowQueryList.clear(); + +flowQueryList.endTransactionAndNotify(); + ``` From 097ba5ffb390471323053de9be9421e60b525347 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 10:44:06 -0400 Subject: [PATCH 08/24] fixed readme example. Now models will use SAVE properly when called in OneToMany. --- .../android/dbflow/processor/Classes.java | 1 + .../definition/OneToManyDefinition.java | 14 ++++- .../processor/definition/TableDefinition.java | 4 +- .../processor/writer/OneToManySaveWriter.java | 58 +++++++++++++++++++ .../android/dbflow/sql/QueryBuilder.java | 2 +- .../android/dbflow/test/example/Ant.java | 38 ++++++++++++ .../android/dbflow/test/example/Colony.java | 20 +++++++ .../dbflow/test/example/ColonyDatabase.java | 14 +++++ .../android/dbflow/test/example/Queen.java | 47 +++++++++++++++ README.md | 8 ++- usage/GettingStarted.md | 32 ++++++---- 11 files changed, 220 insertions(+), 18 deletions(-) create mode 100644 DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/OneToManySaveWriter.java create mode 100644 DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Ant.java create mode 100644 DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Colony.java create mode 100644 DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/ColonyDatabase.java create mode 100644 DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java diff --git a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/Classes.java b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/Classes.java index a0e3adbc1..c11a65820 100644 --- a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/Classes.java +++ b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/Classes.java @@ -109,4 +109,5 @@ public class Classes { public static final String DELETE_MODEL_LIST_TRANSACTION = TRANSACTION + "process.DeleteModelListTransaction"; + public static final String SAVE_MODEL_LIST_TRANSACTION = TRANSACTION + "process.SaveModelTransaction"; } diff --git a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java index 2582c3aa6..b56ebc768 100644 --- a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java +++ b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java @@ -12,6 +12,7 @@ import java.util.List; import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; /** * Description: Represents the {@link OneToMany} annotation. @@ -51,6 +52,10 @@ public boolean isDelete() { return isAll() || methods.contains(OneToMany.Method.DELETE); } + public boolean isSave() { + return isAll() || methods.contains(OneToMany.Method.SAVE); + } + /** * Writes the method to the specified java writer for loading from DB. * @@ -74,7 +79,14 @@ public void writeDelete(JavaWriter javaWriter) throws IOException { javaWriter.emitStatement("new %1s<>(%1s.withModels(%1s)).onExecute()", Classes.DELETE_MODEL_LIST_TRANSACTION, Classes.PROCESS_MODEL_INFO, getMethodName()); - javaWriter.emitStatement("%1s = null", getVariableName()); + } + } + + public void writeSave(JavaWriter javaWriter) throws IOException { + if (isSave()) { + javaWriter.emitStatement("new %1s<>(%1s.withModels(%1s)).onExecute()", + Classes.SAVE_MODEL_LIST_TRANSACTION, + Classes.PROCESS_MODEL_INFO, getMethodName()); } } diff --git a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.java b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.java index 5a31f6fe6..9a648cafb 100644 --- a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.java +++ b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.java @@ -20,6 +20,7 @@ import com.raizlabs.android.dbflow.processor.writer.ExistenceWriter; import com.raizlabs.android.dbflow.processor.writer.FlowWriter; import com.raizlabs.android.dbflow.processor.writer.LoadCursorWriter; +import com.raizlabs.android.dbflow.processor.writer.OneToManySaveWriter; import com.raizlabs.android.dbflow.processor.writer.SQLiteStatementWriter; import com.raizlabs.android.dbflow.processor.writer.WhereQueryWriter; import com.raizlabs.android.dbflow.sql.QueryBuilder; @@ -162,7 +163,8 @@ public TableDefinition(ProcessorManager manager, Element element) { new LoadCursorWriter(this, false, implementsLoadFromCursorListener), new WhereQueryWriter(this, false), new CreationQueryWriter(manager, this), - new DeleteWriter(this, false) + new DeleteWriter(this, false), + new OneToManySaveWriter(this, false) }; // single primary key checking for a long or int valued column diff --git a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/OneToManySaveWriter.java b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/OneToManySaveWriter.java new file mode 100644 index 000000000..faa9b215e --- /dev/null +++ b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/OneToManySaveWriter.java @@ -0,0 +1,58 @@ +package com.raizlabs.android.dbflow.processor.writer; + +import com.google.common.collect.Sets; +import com.raizlabs.android.dbflow.processor.definition.OneToManyDefinition; +import com.raizlabs.android.dbflow.processor.definition.TableDefinition; +import com.raizlabs.android.dbflow.processor.utils.ModelUtils; +import com.raizlabs.android.dbflow.processor.utils.WriterUtils; +import com.squareup.javawriter.JavaWriter; + +import java.io.IOException; + +import javax.lang.model.element.Modifier; + +/** + * Description: Overrides the save, update, and insert methods if the {@link com.raizlabs.android.dbflow.annotation.OneToMany.Method#SAVE} is used. + */ +public class OneToManySaveWriter implements FlowWriter { + + private final TableDefinition tableDefinition; + private final boolean isModelContainerAdapter; + + public OneToManySaveWriter(TableDefinition tableDefinition, boolean isModelContainerAdapter) { + this.tableDefinition = tableDefinition; + this.isModelContainerAdapter = isModelContainerAdapter; + } + + @Override + public void write(JavaWriter javaWriter) throws IOException { + + boolean shouldWrite = false; + for (OneToManyDefinition oneToManyDefinition : tableDefinition.oneToManyDefinitions) { + if(oneToManyDefinition.isSave()) { + shouldWrite = true; + break; + } + } + + if(shouldWrite) { + writeMethod("save", javaWriter); + writeMethod("insert", javaWriter); + writeMethod("update", javaWriter); + } + } + + private void writeMethod(final String methodName, JavaWriter javaWriter) { + WriterUtils.emitOverriddenMethod(javaWriter, new FlowWriter() { + @Override + public void write(JavaWriter javaWriter) throws IOException { + for (OneToManyDefinition oneToManyDefinition : tableDefinition.oneToManyDefinitions) { + oneToManyDefinition.writeSave(javaWriter); + } + + javaWriter.emitStatement("super.%1s(%1s)", methodName, ModelUtils.getVariable(isModelContainerAdapter)); + } + }, "void", methodName, Sets.newHashSet(Modifier.PUBLIC, Modifier.FINAL), tableDefinition.getModelClassName(), + ModelUtils.getVariable(isModelContainerAdapter)); + } +} diff --git a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java index 75fecb10c..2b9715ea9 100644 --- a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java +++ b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java @@ -214,7 +214,7 @@ public String getQuery() { /** * @param columnName The column name to use. - * @return A name in quotes. E.G. index => `index` so we can use keywords as column names without fear + * @return A name in quotes. E.G. index => `index` so we can use keywords as column names without fear * of clashing. */ public static String quote(String columnName) { diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Ant.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Ant.java new file mode 100644 index 000000000..c78b58ade --- /dev/null +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Ant.java @@ -0,0 +1,38 @@ +package com.raizlabs.android.dbflow.test.example; + +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.ForeignKey; +import com.raizlabs.android.dbflow.annotation.ForeignKeyReference; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; +import com.raizlabs.android.dbflow.structure.container.ForeignKeyContainer; + +/** + * Description: + */ +@Table(databaseName = ColonyDatabase.NAME) +public class Ant extends BaseModel { + + @Column + @PrimaryKey(autoincrement = true) + long id; + + @Column + String type; + + @Column + boolean isMale; + + @Column + @ForeignKey(references = {@ForeignKeyReference(columnName = "queen_id", + columnType = Long.class, + foreignColumnName = "id")}, + saveForeignKeyModel = false) + ForeignKeyContainer queenForeignKeyContainer; + + public void associateQueen(Queen queen) { + queenForeignKeyContainer = new ForeignKeyContainer<>(Queen.class); + queenForeignKeyContainer.setModel(queen); + } +} diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Colony.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Colony.java new file mode 100644 index 000000000..abd7f2cd8 --- /dev/null +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Colony.java @@ -0,0 +1,20 @@ +package com.raizlabs.android.dbflow.test.example; + +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +/** + * Description: + */ +@Table(databaseName = ColonyDatabase.NAME) +public class Colony extends BaseModel { + + @Column + @PrimaryKey(autoincrement = true) + long id; + + @Column + String name; +} diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/ColonyDatabase.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/ColonyDatabase.java new file mode 100644 index 000000000..84f6640d6 --- /dev/null +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/ColonyDatabase.java @@ -0,0 +1,14 @@ +package com.raizlabs.android.dbflow.test.example; + +import com.raizlabs.android.dbflow.annotation.Database; + +/** + * Description: + */ +@Database(name = ColonyDatabase.NAME, version = ColonyDatabase.VERSION) +public class ColonyDatabase { + + public static final String NAME = "Colonies"; + + public static final int VERSION = 1; +} diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java new file mode 100644 index 000000000..cf4d8fe1b --- /dev/null +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java @@ -0,0 +1,47 @@ +package com.raizlabs.android.dbflow.test.example; + +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.ForeignKey; +import com.raizlabs.android.dbflow.annotation.ForeignKeyReference; +import com.raizlabs.android.dbflow.annotation.OneToMany; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.sql.builder.Condition; +import com.raizlabs.android.dbflow.sql.language.Select; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import java.util.List; + +/** + * Description: + */ +@Table(databaseName = ColonyDatabase.NAME) +public class Queen extends BaseModel { + + @Column + @PrimaryKey(autoincrement = true) + long id; + + @Column + String name; + + @Column + @ForeignKey(references = {@ForeignKeyReference(columnName = "colony_id", + columnType = Long.class, + foreignColumnName = "id")}, + saveForeignKeyModel = false) + Colony colony; + + private List ants; + + @OneToMany(methods = {OneToMany.Method.ALL}, variableName = "ants") + public List getMyAnts() { + if (ants == null) { + ants = new Select() + .from(Ant.class) + .where(Condition.column(Ant$Table.QUEENFOREIGNKEYCONTAINER_QUEEN_ID).eq(id)) + .queryList(); + } + return ants; + } +} diff --git a/README.md b/README.md index 40e6a2933..4c7f4c443 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ amazing apps. What sets this library apart: 1. Many, many unit tests on nearly __every__ feature. 2. Built on maximum performance using **annotation processing**, lazy-loading, and speed-tests [here](https://github.com/Raizlabs/AndroidDatabaseLibraryComparison) - 3. Built-in model caching for blazing fast retrieval and ability to define own - cache. - 3. Powerful and fluid SQL-wrapping statements + 3. Built-in model caching for blazing-fast retrieval and very flexible customization. + 3. Powerful and fluid SQL-wrapping statements that mimic real SQLite queries 4. Triggers, Views, Indexes, and many more SQLite features. 5. Seamless multi-database support. 6. Direct-to-database parsing for data such as JSON 7. Flexibility in the API enabling you to override functionality to suit your needs. 8. ```ContentProvider``` generation using annotations + 9. Content Observing using `Uri` ## Applications That Use DBFlow @@ -43,6 +43,8 @@ works without crashing/complaining. Just don't call the non-tables associated `M 5. Adds ability to validate values for columns via the [ColumnValueValidator](https://github.com/Raizlabs/DBFlow/blob/master/usage/Conditions.md). 6. Adds the `OrderBy` object to aid in `ORDER BY` queries. Added `orderBy()` methods in the `From` class for easier access without needing to call `where()` first. +7. Adds a `enableSelfRefreshes()` for the `FlowQueryList` and souped up the documentation +with a "best practices" section. #### 2.1.0 diff --git a/usage/GettingStarted.md b/usage/GettingStarted.md index cddbe1540..2df1409dd 100644 --- a/usage/GettingStarted.md +++ b/usage/GettingStarted.md @@ -194,8 +194,15 @@ public class Ant extends BaseModel { columnType = Long.class, foreignColumnName = "id")}, saveForeignKeyModel = false) - ForeignKeyContainer queen; - + ForeignKeyContainer queenModelContainer; + + /** + * Example of setting the model for the queen. + */ + public void associateQueen(Queen queen) { + queenModelContainer = new ForeignKeyContainer<>(Queen.class); + queenModelContainer.setModel(queen); + } } @@ -206,9 +213,11 @@ ant is male or female. We use a `ForeignKeyContainer` in this instance, since we can have thousands of ants. For performance reasons this will "lazy-load" the relationship of the `Queen` and only -run the query on the DB for the `Queen` when we call `toModel()`. Consequently, we must add -the `@ModelContainer` annotation to the `Queen` class and establish the 1-to-many - relationship by lazy-loading the ants: +run the query on the DB for the `Queen` when we call `toModel()`. + +Since `ModelContainer` usage is not generated by default, we must add the `@ModelContainer` annotation to the `Queen` class in order to use for a `ForeignKeyContainer`. + + Next, we establish the 1-to-many relationship by lazy-loading the ants for performance reasons: ```java @@ -217,14 +226,15 @@ the `@ModelContainer` annotation to the `Queen` class and establish the 1-to-man public class Queen extends BaseModel { //... - private List ants; + // needs to be accessible for DELETE + List ants; - @OneToMany(methods = {OneToMany.Method.ALL}) + @OneToMany(methods = {OneToMany.Method.SAVE, OneToMany.Method.DELETE}, variableName = "ants") public List getMyAnts() { if(ants == null) { ants = new Select() .from(Ant.class) - .where(Condition.column(Ant$Table.QUEEN_QUEEN_ID).is(id)) + .where(Condition.column(Ant$Table.QUEENFOREIGNKEYCONTAINER_QUEEN_ID).is(id)) .queryList(); } return ants; @@ -233,7 +243,5 @@ public class Queen extends BaseModel { ``` -This generates a `$Container` adapter class that's not usually generated to cut -down on generated code. - -If you wish to lazy-load the relationship, just leave out the `@OneToMany` annotation. +If you wish to lazy-load the relationship yourself, specify `OneToMany.Method.DELETE` and `SAVE` instead of `ALL`. +If you wish not to save them whenever the `Queen`'s data changes, specify `DELETE` and `LOAD` only. From 869b0cbe82178a52d9981c7a9c347ae6c22837f4 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 11:02:45 -0400 Subject: [PATCH 09/24] updating readme with more changes. --- .../processor/definition/OneToManyDefinition.java | 15 ++++++++------- README.md | 3 +++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java index b56ebc768..3178239c0 100644 --- a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java +++ b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.java @@ -12,7 +12,6 @@ import java.util.List; import javax.lang.model.element.Element; -import javax.lang.model.element.Modifier; /** * Description: Represents the {@link OneToMany} annotation. @@ -63,7 +62,7 @@ public boolean isSave() { * @throws IOException */ public void writeLoad(JavaWriter javaWriter) throws IOException { - if(isLoad()) { + if (isLoad()) { javaWriter.emitStatement(getMethodName()); } } @@ -75,18 +74,20 @@ public void writeLoad(JavaWriter javaWriter) throws IOException { * @throws IOException */ public void writeDelete(JavaWriter javaWriter) throws IOException { - if(isDelete()) { + if (isDelete()) { javaWriter.emitStatement("new %1s<>(%1s.withModels(%1s)).onExecute()", - Classes.DELETE_MODEL_LIST_TRANSACTION, - Classes.PROCESS_MODEL_INFO, getMethodName()); + Classes.DELETE_MODEL_LIST_TRANSACTION, + Classes.PROCESS_MODEL_INFO, getMethodName()); + + javaWriter.emitStatement("%1s = null", getVariableName()); } } public void writeSave(JavaWriter javaWriter) throws IOException { if (isSave()) { javaWriter.emitStatement("new %1s<>(%1s.withModels(%1s)).onExecute()", - Classes.SAVE_MODEL_LIST_TRANSACTION, - Classes.PROCESS_MODEL_INFO, getMethodName()); + Classes.SAVE_MODEL_LIST_TRANSACTION, + Classes.PROCESS_MODEL_INFO, getMethodName()); } } diff --git a/README.md b/README.md index 4c7f4c443..7c0336cb5 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ works without crashing/complaining. Just don't call the non-tables associated `M for easier access without needing to call `where()` first. 7. Adds a `enableSelfRefreshes()` for the `FlowQueryList` and souped up the documentation with a "best practices" section. +8. Fixes bugs with the [Getting Started](https://github.com/Raizlabs/DBFlow/blob/master/usage/GettingStarted.md) section implementation. `OneToMany.Method.SAVE` now actually works on `insert`, `update`, and `save` methods. +9. Fixes an issue where using a autoincrementing primary key with `length()` causes a strange exception. Now we +ignore columns with length specified on autoincrementing primary keys. #### 2.1.0 From d78466038fa4266670a2597efb34d1bbb500bdca Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 11:16:21 -0400 Subject: [PATCH 10/24] fix compile issue. Added to ProcessModelTransaction a listener for progress updates --- .../android/dbflow/test/example/Queen.java | 2 +- .../process/ProcessModelTransaction.java | 44 ++++++++++++++++++- README.md | 4 +- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java index cf4d8fe1b..f6d864ec4 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java @@ -32,7 +32,7 @@ public class Queen extends BaseModel { saveForeignKeyModel = false) Colony colony; - private List ants; + List ants; @OneToMany(methods = {OneToMany.Method.ALL}, variableName = "ants") public List getMyAnts() { diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/transaction/process/ProcessModelTransaction.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/transaction/process/ProcessModelTransaction.java index 007ce4429..5b59f85d2 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/transaction/process/ProcessModelTransaction.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/transaction/process/ProcessModelTransaction.java @@ -1,7 +1,7 @@ package com.raizlabs.android.dbflow.runtime.transaction.process; -import com.raizlabs.android.dbflow.runtime.transaction.BaseResultTransaction; import com.raizlabs.android.dbflow.runtime.FlowContentObserver; +import com.raizlabs.android.dbflow.runtime.transaction.BaseResultTransaction; import com.raizlabs.android.dbflow.structure.Model; import java.util.List; @@ -13,10 +13,28 @@ public abstract class ProcessModelTransaction extends BaseResultTransaction> implements ProcessModel { + /** + * Called during execution of the {@link #processModel(Model)} method. Gives an idea of progress. + */ + public interface OnProcessProgressChangeListener { + + /** + * @param current The current index of execution. + * @param maxProgress The maximum progress (total count of models) + * @param modifiedModel The model that was modified. + */ + void onProcessProgressChange(long current, long maxProgress, ModelClass modifiedModel); + } + protected ProcessModelInfo processModelInfo; private final FlowContentObserver contentObserver; + private long count = 0; + private final long totalCount; + + private OnProcessProgressChangeListener changeListener; + /** * Constructs this transaction with a single model enabled. * @@ -27,6 +45,18 @@ public ProcessModelTransaction(ProcessModelInfo modelInfo, FlowConte super(modelInfo.getInfo(), modelInfo.transactionListener); processModelInfo = modelInfo; this.contentObserver = contentObserver; + + totalCount = processModelInfo.models.size(); + } + + /** + * Registers a listener to get callbacks during the {@link #processModel(Model)} operation when this + * transaction is executed. + * + * @param changeListener The listener to call. + */ + public void setChangeListener(OnProcessProgressChangeListener changeListener) { + this.changeListener = changeListener; } @Override @@ -40,7 +70,17 @@ public List onExecute() { if (contentObserver != null) { contentObserver.beginTransaction(); } - processModelInfo.processModels(this); + processModelInfo.processModels(new ProcessModel() { + @Override + public void processModel(ModelClass model) { + ProcessModelTransaction.this.processModel(model); + count++; + + if (changeListener != null) { + changeListener.onProcessProgressChange(count, totalCount, model); + } + } + }); List models = processModelInfo.models; if (contentObserver != null) { contentObserver.endTransactionAndNotify(); diff --git a/README.md b/README.md index 7c0336cb5..5924632dd 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ for easier access without needing to call `where()` first. 7. Adds a `enableSelfRefreshes()` for the `FlowQueryList` and souped up the documentation with a "best practices" section. 8. Fixes bugs with the [Getting Started](https://github.com/Raizlabs/DBFlow/blob/master/usage/GettingStarted.md) section implementation. `OneToMany.Method.SAVE` now actually works on `insert`, `update`, and `save` methods. -9. Fixes an issue where using a autoincrementing primary key with `length()` causes a strange exception. Now we -ignore columns with length specified on autoincrementing primary keys. +9. Adds a `OnProgressProcessChangeListener` to listen for the total progress while +looping through saving models in a `ProcessModelTransaction`. #### 2.1.0 From 6bc63a7f102eab1ad59b3128fc1ebe7a2049d9e0 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 11:23:56 -0400 Subject: [PATCH 11/24] added collate to OrderBy and fixed bugs with it. --- .../android/dbflow/sql/QueryBuilder.java | 11 +++-- .../android/dbflow/sql/language/OrderBy.java | 44 ++++++++++++++++--- .../android/dbflow/sql/language/Where.java | 4 +- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java index 2b9715ea9..abdc38b40 100644 --- a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java +++ b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java @@ -41,12 +41,12 @@ public QueryClass appendSpace() { /** * Appends the string with spaces on the front and end of the string * - * @param string The string to append + * @param object The object to append * @return This instance */ @SuppressWarnings("unchecked") - public QueryClass appendSpaceSeparated(String string) { - return (QueryClass) appendSpace().append(string).appendSpace(); + public QueryClass appendSpaceSeparated(Object object) { + return (QueryClass) appendSpace().append(object).appendSpace(); } /** @@ -146,7 +146,10 @@ public QueryClass appendList(List objects) { */ public QueryClass appendQualifier(String name, String value) { if (value != null && value.length() > 0) { - append(name).appendSpaceSeparated(value); + if(name != null) { + append(name); + } + appendSpaceSeparated(value); } return castThis(); } diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java index b8ff8663d..237ff7cf0 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java @@ -1,5 +1,8 @@ package com.raizlabs.android.dbflow.sql.language; +import android.text.TextUtils; + +import com.raizlabs.android.dbflow.annotation.Collate; import com.raizlabs.android.dbflow.sql.Query; import com.raizlabs.android.dbflow.sql.QueryBuilder; @@ -16,6 +19,10 @@ public class OrderBy implements Query { private boolean ascending = false; + private Collate orderByCollation; + + private String stringOrderBy; + private OrderBy() { } @@ -42,6 +49,17 @@ public static OrderBy columns(String... columnNames) { return orderBy; } + /** + * Internal usage only. + * @param orderBy + * @return A new order by with the specified string. + */ + static OrderBy fromString(String orderByString) { + OrderBy orderBy = new OrderBy(); + orderBy.stringOrderBy = orderByString; + return orderBy; + } + /** * @return Orders the results in ascending order. */ @@ -56,6 +74,15 @@ public OrderBy descending() { return setAscending(false); } + /** + * @param collate The {@link Collate} to append to the end of this clause. + * @return This instance with a {@link Collate} appended to the end. + */ + public OrderBy collation(Collate collate) { + orderByCollation = collate; + return this; + } + /** * @param isAscending if the ORDER BY is ascending. * @return This instance if its ascending or descending. @@ -68,13 +95,20 @@ public OrderBy setAscending(boolean isAscending) { @Override public String getQuery() { QueryBuilder queryBuilder = new QueryBuilder("ORDER BY "); - for (int i = 0; i < columnAliasList.size(); i++) { - if (i > 0) { - queryBuilder.append(", "); + if (!TextUtils.isEmpty(stringOrderBy)) { + queryBuilder.append(stringOrderBy); + } else { + for (int i = 0; i < columnAliasList.size(); i++) { + if (i > 0) { + queryBuilder.append(", "); + } + queryBuilder.append(columnAliasList.get(i).getAliasName()); + } + queryBuilder.appendSpace().append(ascending ? "ASC" : "DESC"); + if (orderByCollation != null) { + queryBuilder.appendSpace().appendSpaceSeparated(orderByCollation); } - queryBuilder.append(columnAliasList.get(i).getAliasName()); } - queryBuilder.appendSpace().append(ascending ? "ASC" : "DESC"); return queryBuilder.getQuery(); } } diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java index adad26d43..f3d4d9373 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java @@ -227,7 +227,7 @@ public Where orderBy(boolean ascending, String... columns) { * @return This WHERE query. */ public Where orderBy(String orderby) { - orderBy = orderby; + orderBy = OrderBy.fromString(orderby).getQuery(); return this; } @@ -296,7 +296,7 @@ public String getQuery() { .appendQualifier("WHERE", conditionQueryBuilder.getQuery()) .appendQualifier("GROUP BY", groupBy) .appendQualifier("HAVING", having.getQuery()) - .appendQualifier("ORDER BY", orderBy) + .appendQualifier(null, orderBy) .appendQualifier("LIMIT", limit) .appendQualifier("OFFSET", offset); From ddd1e1b170ff8909aeec3840f696f247ec54dbf3 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 11:30:32 -0400 Subject: [PATCH 12/24] compress noisy exception by using WARNING in the catch block instead --- .../android/dbflow/processor/model/ProcessorManager.java | 4 ++-- .../com/raizlabs/android/dbflow/sql/language/OrderBy.java | 2 +- README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/model/ProcessorManager.java b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/model/ProcessorManager.java index a9ed495c0..8872fa5f8 100644 --- a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/model/ProcessorManager.java +++ b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/model/ProcessorManager.java @@ -294,7 +294,7 @@ public void handle(ProcessorManager processorManager, RoundEnvironment roundEnvi databaseWriter.write(javaWriter); javaWriter.close(); } catch (IOException e) { - e.printStackTrace(); + processorManager.getMessager().printMessage(Diagnostic.Kind.WARNING, e.getMessage()); } } @@ -308,7 +308,7 @@ public void handle(ProcessorManager processorManager, RoundEnvironment roundEnvi staticFlowManager.close(); } catch (IOException e) { - e.printStackTrace(); + processorManager.getMessager().printMessage(Diagnostic.Kind.WARNING, e.getMessage()); } } } diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java index 237ff7cf0..8fac3687e 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java @@ -78,7 +78,7 @@ public OrderBy descending() { * @param collate The {@link Collate} to append to the end of this clause. * @return This instance with a {@link Collate} appended to the end. */ - public OrderBy collation(Collate collate) { + public OrderBy collate(Collate collate) { orderByCollation = collate; return this; } diff --git a/README.md b/README.md index 5924632dd..b668cfff3 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ works without crashing/complaining. Just don't call the non-tables associated `M 4. Bug fixes and Improvements 5. Adds ability to validate values for columns via the [ColumnValueValidator](https://github.com/Raizlabs/DBFlow/blob/master/usage/Conditions.md). 6. Adds the `OrderBy` object to aid in `ORDER BY` queries. Added `orderBy()` methods in the `From` class -for easier access without needing to call `where()` first. +for easier access without needing to call `where()` first. Adds `Collate` support within this class. 7. Adds a `enableSelfRefreshes()` for the `FlowQueryList` and souped up the documentation with a "best practices" section. 8. Fixes bugs with the [Getting Started](https://github.com/Raizlabs/DBFlow/blob/master/usage/GettingStarted.md) section implementation. `OneToMany.Method.SAVE` now actually works on `insert`, `update`, and `save` methods. From 1ce9d562e922d1439fff6503e6655a3a09039f06 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 14:09:12 -0400 Subject: [PATCH 13/24] removing validator for another day. --- .../annotation/ColumnValueValidator.java | 41 ------------------- .../sql/validation/ColumnValueValidator.java | 14 ------- .../InvalidColumnValueException.java | 11 ----- app/build.gradle | 6 +++ .../raizlabs/android/dbflow/DemoActivity.java | 3 ++ 5 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java delete mode 100644 DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java delete mode 100644 DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java diff --git a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java b/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java deleted file mode 100644 index e2e0a4678..000000000 --- a/DBFlow-Core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnValueValidator.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Registers a ColumnValueValidator with DBFlow to be applied to - * a specific column from a specific table. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface ColumnValueValidator { - - /** - * If set (default), if an invalid value is passed, we will throw an exception. - */ - int HANDLING_CRASH = -1; - - /** - * If set, the default value for a column is used. - */ - int HANDLING_USE_DEFAULT = 0; - - /** - * If set, you can specify a replacement string to be used in a more graceful fallback. - */ - int HANDLING_SPECIFY_STRING = 1; - - /** - * @return {@link #HANDLING_CRASH} by default. Specify how the column value falls back. - */ - int handling() default HANDLING_CRASH; - - /** - * @return The value you wish to use to fallback in the case the {@link #handling()} is {@link #HANDLING_SPECIFY_STRING} - * and the ColumnValueValidator fails. - */ - String stringFallback() default ""; -} diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java deleted file mode 100644 index 405b8e74d..000000000 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/ColumnValueValidator.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.raizlabs.android.dbflow.sql.validation; - -/** - * Description: This class's sole purpose is to validate column values and - * react to outliers or invalid data via SQL injection. - */ -public abstract class ColumnValueValidator { - - /** - * @return true if the specified value is a valid value, false if not. - */ - public abstract boolean isValid(Object value); - -} diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java deleted file mode 100644 index fae6b7d67..000000000 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/validation/InvalidColumnValueException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.raizlabs.android.dbflow.sql.validation; - -/** - * Description: Thrown when an invalid column value is passed in a {@link ColumnValueValidator}. - */ -public class InvalidColumnValueException extends RuntimeException { - - public InvalidColumnValueException(String detailMessage) { - super(detailMessage); - } -} diff --git a/app/build.gradle b/app/build.gradle index 28fbc8818..02cd7533d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,12 @@ android { packagingOptions { exclude 'META-INF/services/javax.annotation.processing.Processor' } + + buildTypes { + debug{ + minifyEnabled true + } + } } dependencies { diff --git a/app/src/main/java/com/raizlabs/android/dbflow/DemoActivity.java b/app/src/main/java/com/raizlabs/android/dbflow/DemoActivity.java index 47d40d014..2ec33fb13 100644 --- a/app/src/main/java/com/raizlabs/android/dbflow/DemoActivity.java +++ b/app/src/main/java/com/raizlabs/android/dbflow/DemoActivity.java @@ -6,6 +6,7 @@ import android.view.MenuItem; import com.raizlabs.android.dbflow.app.R; +import com.raizlabs.android.dbflow.config.FlowManager; public class DemoActivity extends Activity { @@ -14,6 +15,8 @@ public class DemoActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_demo); + + FlowManager.init(getApplicationContext()); } From 345dac555a02f4d43bff0201fdd4c000803e0a03 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 14:12:48 -0400 Subject: [PATCH 14/24] changed function method/column name to an internal ColumnAlias --- .../android/dbflow/sql/language/Select.java | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Select.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Select.java index ef19025d1..f9685e680 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Select.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Select.java @@ -39,15 +39,7 @@ public class Select implements Query { */ private String[] rawColumns; - /** - * The method name we wish to execute - */ - private String mMethodName; - - /** - * The column name passed into the method name - */ - private String mColumnName; + private ColumnAlias columnMethodAlias; /** * Empty constructor since other constructors have different vargs. @@ -122,33 +114,26 @@ public Select selectQualifier(int qualifierInt) { } /** - * appends {@link #ALL} to the query - * - * @return + * @return appends {@link #ALL} to the query */ public Select all() { return selectQualifier(ALL); } /** - * appends COUNT to the query - * - * @return + * @return appends COUNT to the query */ public Select count() { return method("COUNT", "*"); } /** - * Appends a method to this query. Such methods as avg, count, sum. - * - * @param methodName - * @param columnName - * @return + * @param methodName The name of the method. + * @param columnName The name of the column to use. + * @return Appends a method to this query. Such methods as avg, count, sum. */ public Select method(String methodName, String columnName) { - mMethodName = methodName; - mColumnName = columnName; + columnMethodAlias = ColumnAlias.columnsWithFunction(methodName, columnName); return selectQualifier(METHOD); } @@ -187,7 +172,7 @@ public String getQuery() { } else if (mSelectQualifier == ALL) { queryBuilder.append("ALL"); } else if (mSelectQualifier == METHOD) { - queryBuilder.append(mMethodName.toUpperCase()).append("(").appendQuoted(mColumnName).append(")"); + queryBuilder.append(columnMethodAlias.getQuery()); } queryBuilder.appendSpace(); } From 897720f0ded809924ba0ac67d883d7d6ced01eac Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 14:49:52 -0400 Subject: [PATCH 15/24] escalated some methods in SQLUtils. Added hasCachingId() method, updated readme with the change --- .../processor/writer/LoadCursorWriter.java | 8 ++ .../raizlabs/android/dbflow/sql/SqlUtils.java | 131 ++++++++++++------ .../dbflow/structure/InternalAdapter.java | 5 + .../dbflow/structure/ModelAdapter.java | 5 + .../container/ModelContainerAdapter.java | 5 + README.md | 2 + 6 files changed, 115 insertions(+), 41 deletions(-) diff --git a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/LoadCursorWriter.java b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/LoadCursorWriter.java index 14860ba2a..416263d80 100644 --- a/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/LoadCursorWriter.java +++ b/DBFlow-Compiler/src/main/java/com/raizlabs/android/dbflow/processor/writer/LoadCursorWriter.java @@ -92,6 +92,14 @@ public void write(JavaWriter javaWriter) throws IOException { final TableDefinition tableDefinition = ((TableDefinition) baseTableDefinition); if (tableDefinition.hasCachingId) { + + WriterUtils.emitOverriddenMethod(javaWriter, new FlowWriter() { + @Override + public void write(JavaWriter javaWriter) throws IOException { + javaWriter.emitStatement("return true"); + } + }, "boolean", "hasCachingId", Sets.newHashSet(Modifier.PUBLIC)); + String[] params2 = new String[2]; params2[0] = ModelUtils.getParameter(isModelContainerDefinition, tableDefinition.getModelClassName()); params2[1] = ModelUtils.getVariable(isModelContainerDefinition); diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java index 154354dea..6bd71b260 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java @@ -10,34 +10,38 @@ import com.raizlabs.android.dbflow.annotation.ConflictAction; import com.raizlabs.android.dbflow.config.BaseDatabaseDefinition; import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.runtime.DBTransactionQueue; import com.raizlabs.android.dbflow.runtime.FlowContentObserver; import com.raizlabs.android.dbflow.sql.language.Delete; -import com.raizlabs.android.dbflow.structure.BaseModel; +import com.raizlabs.android.dbflow.structure.BaseModel.Action; import com.raizlabs.android.dbflow.structure.InstanceAdapter; import com.raizlabs.android.dbflow.structure.InternalAdapter; import com.raizlabs.android.dbflow.structure.Model; import com.raizlabs.android.dbflow.structure.ModelAdapter; import com.raizlabs.android.dbflow.structure.RetrievalAdapter; import com.raizlabs.android.dbflow.structure.cache.BaseCacheableModel; +import com.raizlabs.android.dbflow.structure.cache.ModelCache; +import com.raizlabs.android.dbflow.structure.container.ModelContainer; +import com.raizlabs.android.dbflow.structure.container.ModelContainerAdapter; import java.util.ArrayList; import java.util.List; /** * Description: Provides some handy methods for dealing with SQL statements. It's purpose is to move the - * methods away from the {@link com.raizlabs.android.dbflow.structure.Model} class and let any class use these. + * methods away from the {@link Model} class and let any class use these. */ public class SqlUtils { /** - * Queries the DB for a {@link android.database.Cursor} and converts it into a list. + * Queries the DB for a {@link Cursor} and converts it into a list. * * @param modelClass The class to construct the data from the DB into * @param sql The SQL command to perform, must not be ; terminated. * @param args You may include ?s in where clause in the query, * which will be replaced by the values from selectionArgs. The * values will be bound as Strings. - * @param The class implements {@link com.raizlabs.android.dbflow.structure.Model} + * @param The class implements {@link Model} * @return a list of {@link ModelClass} */ @SuppressWarnings("unchecked") @@ -55,11 +59,27 @@ public static List queryList(Class List convertToCacheableList( - Class modelClass, Cursor cursor) { + /** + * Loops through a {@link Cursor} and builds a list of {@link CacheableClass} objects. If an item + * with the same id exists within the cache for that model, the cached object for that class is used. + * + * @param modelClass The class to convert the cursor into {@link CacheableClass} + * @param cursor The cursor from a query. + * @param modelCache The model cache to use when retrieving {@link CacheableClass}. + * @param The class that extends {@link BaseCacheableModel} + * @return A {@link List} of {@link CacheableClass}. + */ + public static List convertToCacheableList( + Class modelClass, Cursor cursor, ModelCache modelCache) { final List entities = new ArrayList<>(); ModelAdapter instanceAdapter = FlowManager.getModelAdapter(modelClass); if (instanceAdapter != null) { + if (!instanceAdapter.hasCachingId()) { + throw new IllegalArgumentException("You cannot call this method for a table that has no caching id. Either" + + "use one Primary Key or call convertToList()"); + } else if (modelCache == null) { + throw new IllegalArgumentException("ModelCache specified in convertToCacheableList() must not be null."); + } synchronized (cursor) { // Ensure that we aren't iterating over this cursor concurrently from different threads if (cursor.moveToFirst()) { @@ -67,7 +87,7 @@ private static List long id = cursor.getLong(cursor.getColumnIndex(instanceAdapter.getCachingColumnName())); // if it exists in cache no matter the query we will use that one - CacheableClass cacheable = BaseCacheableModel.getCache(modelClass).get(id); + CacheableClass cacheable = modelCache.get(id); if (cacheable != null) { entities.add(cacheable); } else { @@ -82,6 +102,20 @@ private static List return entities; } + /** + * Loops through a {@link Cursor} and builds a list of {@link CacheableClass} objects. If an item + * with the same id exists within the cache for that model, the cached object for that class is used. + * + * @param modelClass The class to convert the cursor into {@link CacheableClass} + * @param cursor The cursor from a query. + * @param The class that extends {@link BaseCacheableModel} + * @return A {@link List} of {@link CacheableClass}. + */ + public static List convertToCacheableList( + Class modelClass, Cursor cursor) { + return convertToCacheableList(modelClass, cursor, BaseCacheableModel.getCache(modelClass)); + } + /** * Loops through a cursor and builds a list of {@link ModelClass} objects. * @@ -117,8 +151,8 @@ public static List convertToList(Class The class that implements {@link com.raizlabs.android.dbflow.structure.Model} - * @return A model transformed from the {@link android.database.Cursor} + * @param The class that implements {@link Model} + * @return A model transformed from the {@link Cursor} */ @SuppressWarnings("unchecked") public static ModelClass convertToModel(boolean dontMoveToFirst, Class table, @@ -142,8 +176,8 @@ public static ModelClass convertToModel(boolean dontM * @param dontMoveToFirst If it's a list or at a specific position, do not reset the cursor * @param table The model class that we convert the cursor data into. * @param cursor The cursor from the DB - * @param The class that implements {@link com.raizlabs.android.dbflow.structure.Model} - * @return A model transformed from the {@link android.database.Cursor} + * @param The class that implements {@link Model} + * @return A model transformed from the {@link Cursor} */ @SuppressWarnings("unchecked") public static CacheableClass convertToCacheableModel( @@ -167,7 +201,7 @@ public static CacheableClass convert } /** - * Queries the DB and returns the first {@link com.raizlabs.android.dbflow.structure.Model} it finds. Note: + * Queries the DB and returns the first {@link Model} it finds. Note: * this may return more than one object, but only will return the first item in the list. * * @param modelClass The class to construct the data from the DB into @@ -175,7 +209,7 @@ public static CacheableClass convert * @param args You may include ?s in where clause in the query, * which will be replaced by the values from selectionArgs. The * values will be bound as Strings. - * @param The class implements {@link com.raizlabs.android.dbflow.structure.Model} + * @param The class implements {@link Model} * @return a single {@link ModelClass} */ @SuppressWarnings("unchecked") @@ -194,13 +228,13 @@ public static ModelClass querySingle(Class The class that implements {@link com.raizlabs.android.dbflow.structure.Model} + * @param The class that implements {@link Model} * @return */ public static boolean hasData(Class table, String sql, String... args) { @@ -234,10 +268,7 @@ void save(TableClass model, AdapterClass adapter, ModelAdapter model insert(model, adapter, modelAdapter); } - if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.SAVE, modelAdapter.getCachingColumnName(), - adapter.getCachingId(model)); - } + notifyModelChanged(model, adapter, modelAdapter, Action.SAVE); } /** @@ -246,7 +277,7 @@ void save(TableClass model, AdapterClass adapter, ModelAdapter model * @param model The model to update * @param modelAdapter The adapter to use * @return true if model was inserted, false if not. Also false could mean that it is placed on the - * {@link com.raizlabs.android.dbflow.runtime.DBTransactionQueue} using async to true. + * {@link DBTransactionQueue} using async to true. */ @SuppressWarnings("unchecked") public static @@ -263,15 +294,14 @@ boolean update(TableClass model, AdapterClass adapter, ModelAdapter if (!exists) { // insert insert(model, adapter, modelAdapter); - } else if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.UPDATE, - modelAdapter.getCachingColumnName(), adapter.getCachingId(model)); + } else { + notifyModelChanged(model, adapter, modelAdapter, Action.UPDATE); } return exists; } /** - * Will attempt to insert the {@link com.raizlabs.android.dbflow.structure.container.ModelContainer} into the DB. + * Will attempt to insert the {@link ModelContainer} into the DB. * * @param model The model to insert. * @param modelAdapter The adapter to use. @@ -283,15 +313,12 @@ void insert(TableClass model, AdapterClass adapter, ModelAdapter mod adapter.bindToStatement(insertStatement, model); long id = insertStatement.executeInsert(); adapter.updateAutoIncrement(model, id); - if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.INSERT, - modelAdapter.getCachingColumnName(), adapter.getCachingId(model)); - } + notifyModelChanged(model, adapter, modelAdapter, Action.INSERT); } /** - * Deletes {@link com.raizlabs.android.dbflow.structure.Model} from the database using the specfied {@link com.raizlabs.android.dbflow.config.FlowManager} + * Deletes {@link Model} from the database using the specfied {@link FlowManager} * * @param model The model to delete */ @@ -301,29 +328,51 @@ void delete(final TableClass model, AdapterClass adapter, ModelAdapter) adapter.getModelClass()).where( adapter.getPrimaryModelWhere(model)).query(); adapter.updateAutoIncrement(model, 0); - if (FlowContentObserver.shouldNotify()) { - notifyModelChanged(modelAdapter.getModelClass(), BaseModel.Action.DELETE, - modelAdapter.getCachingColumnName(), adapter.getCachingId(model)); - } + notifyModelChanged(model, adapter, modelAdapter, Action.DELETE); } /** * Notifies the {@link android.database.ContentObserver} that the model has changed. * - * @param action The {@link com.raizlabs.android.dbflow.structure.BaseModel.Action} enum + * @param action The {@link Action} enum * @param table The table of the model */ - public static void notifyModelChanged(Class table, BaseModel.Action action, String notifyKey, Object notifyValue) { + public static void notifyModelChanged(Class table, Action action, String notifyKey, Object notifyValue) { FlowManager.getContext().getContentResolver().notifyChange(getNotificationUri(table, action, notifyKey, notifyValue), null, true); } + /** + * Performs necessary logic to notify of {@link Model} changes. + * + * @param model The model to use to notify (if a caching id exists for that model). + * @param adapter The adapter to use thats either a {@link ModelAdapter} or {@link ModelContainerAdapter} + * to handle interactions. + * @param modelAdapter The actual {@link ModelAdapter} associated with the {@link ModelClass}/ + * @param action The {@link Action} that occured. + * @param The original model class. + * @param The class of the adapter that we use the model from. + * @param The class of the adapter, which is either a {@link ModelAdapter} or {@link ModelContainerAdapter} + */ + @SuppressWarnings("unchecked") + private static + void notifyModelChanged(TableClass model, AdapterClass adapter, ModelAdapter modelAdapter, Action action) { + if (FlowContentObserver.shouldNotify()) { + if (modelAdapter.hasCachingId()) { + notifyModelChanged(modelAdapter.getModelClass(), action, + modelAdapter.getCachingColumnName(), adapter.getCachingId(model)); + } else { + notifyModelChanged(modelAdapter.getModelClass(), action, null, null); + } + } + } + /** * Returns the uri for notifications from model changes * * @param modelClass * @return */ - public static Uri getNotificationUri(Class modelClass, BaseModel.Action action, String notifyKey, Object notifyValue) { + public static Uri getNotificationUri(Class modelClass, Action action, String notifyKey, Object notifyValue) { Uri.Builder uriBuilder = new Uri.Builder().scheme("dbflow") .authority(FlowManager.getTableName(modelClass)); if (action != null) { @@ -337,10 +386,10 @@ public static Uri getNotificationUri(Class modelClass, BaseMode /** * @param modelClass The model class to use. - * @param action The {@link BaseModel.Action} to use. + * @param action The {@link Action} to use. * @return The uri for updates to {@link Model}, meant for general changes. */ - public static Uri getNotificationUri(Class modelClass, BaseModel.Action action) { + public static Uri getNotificationUri(Class modelClass, Action action) { return getNotificationUri(modelClass, action, null, null); } @@ -350,7 +399,7 @@ public static Uri getNotificationUri(Class modelClass, BaseMode * * @param mOnTable The table that this trigger runs on * @param triggerName The name of the trigger - * @param The class that implements {@link com.raizlabs.android.dbflow.structure.Model} + * @param The class that implements {@link Model} */ public static void dropTrigger(Class mOnTable, String triggerName) { QueryBuilder queryBuilder = new QueryBuilder("DROP TRIGGER IF EXISTS ") @@ -363,7 +412,7 @@ public static void dropTrigger(Class mOnT * * @param mOnTable The table that this index runs on * @param indexName The name of the index. - * @param The class that implements {@link com.raizlabs.android.dbflow.structure.Model} + * @param The class that implements {@link Model} */ public static void dropIndex(Class mOnTable, String indexName) { QueryBuilder queryBuilder = new QueryBuilder("DROP INDEX IF EXISTS ") diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java index 112b79540..2c8a9a53a 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java @@ -94,4 +94,9 @@ public interface InternalAdapter model) { return getAutoIncrementingId(model); } + @Override + public boolean hasCachingId() { + return false; + } + /** * Returns the type of the column for this model container. It's useful for when we do not know the exact class of the column * when in a {@link com.raizlabs.android.dbflow.structure.container.ModelContainer} diff --git a/README.md b/README.md index b668cfff3..1388f8a07 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ with a "best practices" section. 8. Fixes bugs with the [Getting Started](https://github.com/Raizlabs/DBFlow/blob/master/usage/GettingStarted.md) section implementation. `OneToMany.Method.SAVE` now actually works on `insert`, `update`, and `save` methods. 9. Adds a `OnProgressProcessChangeListener` to listen for the total progress while looping through saving models in a `ProcessModelTransaction`. +10. Escalated `convertToCacheableList()` to `public` and now can query to know if +a `Model` has a valid caching id. Also some more public methods added to `SqlUtils`! #### 2.1.0 From d8afac1e767453910e537fe85558693a31cc7e65 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 14:59:59 -0400 Subject: [PATCH 16/24] fixed bug where column names not quoted. and uses alias name in columns with function mewthod --- .../android/dbflow/sql/language/ColumnAlias.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java index 3aa42d305..5bd815f34 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java @@ -38,7 +38,7 @@ public static ColumnAlias columnWithTable(String tableName, String columnName) { * EX: date(`myColumn`) -> ColumnAlias.columnsWithFunction("date", "myColumn") */ public static ColumnAlias columnsWithFunction(String functionName, String... columnNames) { - return columnRaw(functionName + "(" + QueryBuilder.join("`, `", columnNames) + ")"); + return columnRaw(new QueryBuilder<>(functionName).append("(").appendQuotedArray(columnNames).append(")").getQuery()); } /** @@ -48,7 +48,15 @@ public static ColumnAlias columnsWithFunction(String functionName, String... col * EX: date(`myColumn`) -> ColumnAlias.columnsWithFunction("date", ColumnAlias.column("myColumn")) */ public static ColumnAlias columnsWithFunction(String functionName, ColumnAlias... columnAliases) { - return columnRaw(functionName + "(" + QueryBuilder.join("`, `", columnAliases) + ")"); + QueryBuilder queryBuilder = new QueryBuilder<>(functionName).append("("); + for (int i = 0; i < columnAliases.length; i++) { + if (i > 0) { + queryBuilder.append(","); + } + queryBuilder.appendQuoted(columnAliases[i].getAliasName()); + } + queryBuilder.append(")"); + return columnRaw(queryBuilder.getQuery()); } /** From 5776c31c7b698a53a0ec9e2af567b686d6d85413 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 16:27:56 -0400 Subject: [PATCH 17/24] fixed bug where notification URI method was not called properly --- .../raizlabs/android/dbflow/runtime/FlowContentObserver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java index 3d4987bce..dcd682eab 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java @@ -130,7 +130,7 @@ public void endTransactionAndNotify() { if (isInTransaction) { isInTransaction = false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { onChange(true); } else { synchronized (notificationUris) { From 633eea78858f1b9e6a9e03228365a272bfd0e2b2 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 16:29:38 -0400 Subject: [PATCH 18/24] api level set for specific changes --- .../raizlabs/android/dbflow/runtime/FlowContentObserver.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java index dcd682eab..f48febf62 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java @@ -67,6 +67,11 @@ public interface OnModelStateChangedListener { void onModelStateChanged(Class table, BaseModel.Action action); } + /** + * Listens for specific model changes. This is only available in {@link android.os.Build.VERSION_CODES#JELLY_BEAN} + * or higher due to the api of {@link ContentObserver}. + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public interface OnSpecificModelStateChangedListener { /** From c430d43a6758ce20d389fdba1e98f6b3310831b2 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 16:40:01 -0400 Subject: [PATCH 19/24] better integration steps with specying the apt plugin on classpath --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1388f8a07..9c8af234f 100644 --- a/README.md +++ b/README.md @@ -109,8 +109,23 @@ Listed here are tutorial screen casts for DBFlow. If more are created, they may ## Including in your project +We need to include the [apt plugin](https://bitbucket.org/hvisser/android-apt) in our classpath to enable Annotation Processing: -Add the library to the project-level build.gradle, using the [apt plugin](https://bitbucket.org/hvisser/android-apt) to enable Annotation Processing: +```groovy + +buildscript { + repositories { + // required for this library, don't use mavenCentral() + jcenter() + } + dependencies { + 'com.neenbedankt.gradle.plugins:android-apt:1.4' + } +} + +``` + +Add the library to the project-level build.gradle, using the to enable Annotation Processing: ```groovy From 7234bd1397a9d874df0a137aaf2787d84e1f6939 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 17:33:54 -0400 Subject: [PATCH 20/24] minor doc improvements in FlowContentObserver --- .../dbflow/runtime/FlowContentObserver.java | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java index f48febf62..d0e60315d 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java @@ -5,11 +5,12 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Handler; import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.sql.SqlUtils; -import com.raizlabs.android.dbflow.structure.BaseModel; +import com.raizlabs.android.dbflow.structure.BaseModel.Action; import com.raizlabs.android.dbflow.structure.Model; import java.util.ArrayList; @@ -20,20 +21,19 @@ import java.util.Set; /** - * Description: Listens for {@link com.raizlabs.android.dbflow.structure.Model} changes. Register for specific + * Description: Listens for {@link Model} changes. Register for specific * tables with {@link #addModelChangeListener(FlowContentObserver.OnModelStateChangedListener)}. * Provides ability to register and deregister listeners for when data is inserted, deleted, updated, and saved if the device is - * above {@link android.os.Build.VERSION_CODES#JELLY_BEAN}. If below it will only provide one callback. + * above {@link VERSION_CODES#JELLY_BEAN}. If below it will only provide one callback. */ public class FlowContentObserver extends ContentObserver { private static final List OBSERVER_LIST = new ArrayList<>(); - private static boolean forceNotify = false; /** * @return true if we have registered for content changes. Otherwise we do not notify - * in {@link com.raizlabs.android.dbflow.sql.SqlUtils#notifyModelChanged(Class, com.raizlabs.android.dbflow.structure.BaseModel.Action)} + * in {@link SqlUtils} * for efficiency purposes. */ public static boolean shouldNotify() { @@ -56,44 +56,41 @@ public static void setShouldForceNotify(boolean forceNotify) { public interface OnModelStateChangedListener { /** - * Notifies that the state of a {@link com.raizlabs.android.dbflow.structure.Model} + * Notifies that the state of a {@link Model} * has changed for the table this is registered for. * - * @param table The table that this change occurred on. This is ONLY available on {@link Build.VERSION_CODES#JELLY_BEAN} + * @param table The table that this change occurred on. This is ONLY available on {@link VERSION_CODES#JELLY_BEAN} * and up. - * @param action The action on the model. for versions prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN} , - * the {@link com.raizlabs.android.dbflow.structure.BaseModel.Action#CHANGE} will always be called for any action. + * @param action The action on the model. for versions prior to {@link VERSION_CODES#JELLY_BEAN} , + * the {@link Action#CHANGE} will always be called for any action. */ - void onModelStateChanged(Class table, BaseModel.Action action); + void onModelStateChanged(Class table, Action action); } /** - * Listens for specific model changes. This is only available in {@link android.os.Build.VERSION_CODES#JELLY_BEAN} + * Listens for specific model changes. This is only available in {@link VERSION_CODES#JELLY_BEAN} * or higher due to the api of {@link ContentObserver}. */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @TargetApi(VERSION_CODES.JELLY_BEAN) public interface OnSpecificModelStateChangedListener { /** - * Notifies that the state of a {@link com.raizlabs.android.dbflow.structure.Model} + * Notifies that the state of a {@link Model} * has changed for the table this is registered for. * - * @param table The table that this change occurred on. This is ONLY available on {@link Build.VERSION_CODES#JELLY_BEAN} + * @param table The table that this change occurred on. This is ONLY available on {@link VERSION_CODES#JELLY_BEAN} * and up. - * @param action The action on the model. for versions prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN} , - * the {@link com.raizlabs.android.dbflow.structure.BaseModel.Action#CHANGE} will always be called for any action. + * @param action The action on the model. for versions prior to {@link VERSION_CODES#JELLY_BEAN} , + * the {@link Action#CHANGE} will always be called for any action. * @param columnName The name of the primary column with the id that's changed. * @param value the value from the primary key thats changed converted to String. */ - void onModelStateChanged(Class table, BaseModel.Action action, String columnName, String value); + void onModelStateChanged(Class table, Action action, String columnName, String value); } private final List modelChangeListeners = new ArrayList<>(); private final List specificModelChangeListeners = new ArrayList<>(); - - private final Map> registeredTables = new HashMap<>(); - private final Set notificationUris = new HashSet<>(); protected boolean isInTransaction = false; @@ -108,8 +105,8 @@ public FlowContentObserver(Handler handler) { } /** - * If true, this class will get specific when it needs to, such as using all {@link BaseModel.Action} qualifiers. - * If false, it only uses the {@link BaseModel.Action#CHANGE} action in callbacks. + * If true, this class will get specific when it needs to, such as using all {@link Action} qualifiers. + * If false, it only uses the {@link Action#CHANGE} action in callbacks. * * @param notifyAllUris */ @@ -135,7 +132,7 @@ public void endTransactionAndNotify() { if (isInTransaction) { isInTransaction = false; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + if (Build.VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN) { onChange(true); } else { synchronized (notificationUris) { @@ -151,7 +148,7 @@ public void endTransactionAndNotify() { /** * Add a listener for model changes * - * @param modelChangeListener + * @param modelChangeListener Generic model change events from an {@link Action} */ public void addModelChangeListener(OnModelStateChangedListener modelChangeListener) { modelChangeListeners.add(modelChangeListener); @@ -160,7 +157,7 @@ public void addModelChangeListener(OnModelStateChangedListener modelChangeListen /** * Removes a listener for model changes * - * @param modelChangeListener + * @param modelChangeListener Generic model change events from a {@link Action} */ public void removeModelChangeListener(OnModelStateChangedListener modelChangeListener) { modelChangeListeners.remove(modelChangeListener); @@ -169,7 +166,7 @@ public void removeModelChangeListener(OnModelStateChangedListener modelChangeLis /** * Add a specific listener for model changes * - * @param modelChangeListener + * @param modelChangeListener Listens for specific model change events. */ public void addSpecificModelChangeListener(OnSpecificModelStateChangedListener modelChangeListener) { specificModelChangeListeners.add(modelChangeListener); @@ -178,7 +175,7 @@ public void addSpecificModelChangeListener(OnSpecificModelStateChangedListener m /** * Removes a specific listener for model changes * - * @param modelChangeListener + * @param modelChangeListener Listens for specific model change events. */ public void removeSpecificModelChangeListener(OnSpecificModelStateChangedListener modelChangeListener) { specificModelChangeListeners.remove(modelChangeListener); @@ -209,11 +206,11 @@ public void unregisterForContentChanges(Context context) { @Override public void onChange(boolean selfChange) { for (OnModelStateChangedListener modelChangeListener : modelChangeListeners) { - modelChangeListener.onModelStateChanged(null, BaseModel.Action.CHANGE); + modelChangeListener.onModelStateChanged(null, Action.CHANGE); } } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @TargetApi(VERSION_CODES.JELLY_BEAN) @Override public void onChange(boolean selfChange, Uri uri) { String fragment = uri.getFragment(); @@ -236,7 +233,7 @@ public void onChange(boolean selfChange, Uri uri) { Class table = registeredTables.get(tableName); if (!isInTransaction) { - BaseModel.Action action = BaseModel.Action.valueOf(fragment); + Action action = Action.valueOf(fragment); if (action != null) { for (OnModelStateChangedListener modelChangeListener : modelChangeListeners) { modelChangeListener.onModelStateChanged(table, action); @@ -251,7 +248,7 @@ public void onChange(boolean selfChange, Uri uri) { } else { // convert this uri to a CHANGE op if we don't care about individual changes. if (!notifyAllUris) { - uri = SqlUtils.getNotificationUri(table, BaseModel.Action.CHANGE); + uri = SqlUtils.getNotificationUri(table, Action.CHANGE); } synchronized (notificationUris) { // add and keep track of unique notification uris for when transaction completes. From acbaa99057ecacab883528f5f286fc5dc1796238 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 17:34:52 -0400 Subject: [PATCH 21/24] fix issue where alias quoted when it shouldn't have been --- .../com/raizlabs/android/dbflow/sql/language/ColumnAlias.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java index 5bd815f34..698da04ed 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/ColumnAlias.java @@ -53,7 +53,7 @@ public static ColumnAlias columnsWithFunction(String functionName, ColumnAlias.. if (i > 0) { queryBuilder.append(","); } - queryBuilder.appendQuoted(columnAliases[i].getAliasName()); + queryBuilder.append(columnAliases[i].getAliasName()); } queryBuilder.append(")"); return columnRaw(queryBuilder.getQuery()); From 14a8a8b73404a5b5553f9fa38aeef7f80e7dd3cf Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 17:53:53 -0400 Subject: [PATCH 22/24] Updated tests to use static import for Column for readability --- .../container/ContainerDifferenceTest.java | 8 ++--- .../test/container/ForeignKeyModelTest.java | 5 +-- .../dbflow/test/container/MapModelTest.java | 4 +-- .../android/dbflow/test/example/Queen.java | 5 +-- .../test/provider/ContentProviderTest.java | 7 ++-- .../test/querymodel/QueryModelTest.java | 11 +++---- .../dbflow/test/sql/AsyncQueryTest.java | 9 +++--- .../dbflow/test/sql/BlobModelTest.java | 5 +-- .../dbflow/test/sql/BoxedValueTest.java | 5 +-- .../android/dbflow/test/sql/BuilderTest.java | 32 ++++++++++--------- .../android/dbflow/test/sql/IndexTest.java | 5 +-- .../android/dbflow/test/sql/InsertTest.java | 7 ++-- .../dbflow/test/sql/MigrationTest.java | 5 +-- .../android/dbflow/test/sql/SelectTest.java | 19 +++++++---- .../android/dbflow/test/sql/SubqueryTest.java | 10 +++--- .../android/dbflow/test/sql/TriggerTest.java | 11 ++++--- .../dbflow/test/structure/ForeignKeyTest.java | 7 ++-- .../test/structure/ListenerModelTest.java | 5 +-- .../autoincrement/ModelAutoIncrementTest.java | 5 +-- .../structure/caching/CacheableModelTest.java | 9 +++--- .../test/typeconverter/TypeConverterTest.java | 13 ++++---- .../android/dbflow/sql/language/From.java | 11 ++++--- 22 files changed, 112 insertions(+), 86 deletions(-) diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ContainerDifferenceTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ContainerDifferenceTest.java index 63b6bb409..7ab4fe4d2 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ContainerDifferenceTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ContainerDifferenceTest.java @@ -1,11 +1,11 @@ package com.raizlabs.android.dbflow.test.container; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; -import com.raizlabs.android.dbflow.structure.container.ForeignKeyContainer; import com.raizlabs.android.dbflow.test.FlowTestCase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: Asserts values are handled same for container and adapter */ @@ -22,7 +22,7 @@ public void testContainer() { autoIncrementContainer.save(); autoIncrementContainer = new Select().from(AIContainerForeign.class).where( - Condition.column(AIContainerForeign$Table.ID).is(autoIncrementContainer.id)).querySingle(); + column(AIContainerForeign$Table.ID).is(autoIncrementContainer.id)).querySingle(); assertNull(autoIncrementContainer.foreignModel); assertNull(autoIncrementContainer.container); @@ -34,7 +34,7 @@ public void testContainer() { autoIncrementContainer.foreignModel = foreignModel; AutoIncrementContainer foreignKeyContainer = new AutoIncrementContainer(); - foreignKeyContainer.name = "container"; + foreignKeyContainer.name = "container"; foreignKeyContainer.a_id = 54; foreignKeyContainer.save(); assertTrue(foreignKeyContainer.exists()); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ForeignKeyModelTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ForeignKeyModelTest.java index 8e522ba6c..5d958a6b5 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ForeignKeyModelTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/ForeignKeyModelTest.java @@ -1,11 +1,12 @@ package com.raizlabs.android.dbflow.test.container; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.test.FlowTestCase; import com.raizlabs.android.dbflow.test.structure.TestModel1; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -33,7 +34,7 @@ public void testForeignKeyModel() { foreignInteractionModel = new Select().from(ForeignInteractionModel.class) - .where(Condition.column(ForeignInteractionModel$Table.NAME).is("Test2")).querySingle(); + .where(column(ForeignInteractionModel$Table.NAME).is("Test2")).querySingle(); assertNotNull(foreignInteractionModel); assertNotNull(foreignInteractionModel.testModel1); TestModel1 testModel11 = foreignInteractionModel.getTestModel1(); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/MapModelTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/MapModelTest.java index dfb8622f6..763b48932 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/MapModelTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/container/MapModelTest.java @@ -15,7 +15,7 @@ public class MapModelTest extends FlowTestCase { public void testMapModel() { Map dataMap = new HashMap<>(); dataMap.put("name", "test"); - MapModel model1MapModel = new MapModel(dataMap,TestModel1.class); + MapModel model1MapModel = new MapModel<>(dataMap, TestModel1.class); model1MapModel.save(); assertTrue(model1MapModel.exists()); @@ -28,7 +28,7 @@ public void testMapModel() { otherDataMap.put("testModel", dataMap); - MapModel testModelContainerClassMapModel = new MapModel(otherDataMap, TestModelContainerClass.class); + MapModel testModelContainerClassMapModel = new MapModel<>(otherDataMap, TestModelContainerClass.class); testModelContainerClassMapModel.save(); assertTrue(testModelContainerClassMapModel.exists()); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java index f6d864ec4..7e4dc271d 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/example/Queen.java @@ -6,12 +6,13 @@ import com.raizlabs.android.dbflow.annotation.OneToMany; import com.raizlabs.android.dbflow.annotation.PrimaryKey; import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.structure.BaseModel; import java.util.List; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -39,7 +40,7 @@ public List getMyAnts() { if (ants == null) { ants = new Select() .from(Ant.class) - .where(Condition.column(Ant$Table.QUEENFOREIGNKEYCONTAINER_QUEEN_ID).eq(id)) + .where(column(Ant$Table.QUEENFOREIGNKEYCONTAINER_QUEEN_ID).eq(id)) .queryList(); } return ants; diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/provider/ContentProviderTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/provider/ContentProviderTest.java index 811f1b4fe..70ca27bfc 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/provider/ContentProviderTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/provider/ContentProviderTest.java @@ -4,10 +4,11 @@ import android.test.ProviderTestCase2; import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.builder.Condition; +import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.structure.provider.ContentUtils; -import com.raizlabs.android.dbflow.sql.language.Delete; + +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; /** * Description: @@ -104,7 +105,7 @@ public void testSyncableModel() { assertEquals(testSyncableModel.name, "TestName"); testSyncableModel = new Select().from(TestSyncableModel.class) - .where(Condition.column(TestSyncableModel$Table.ID).is(testSyncableModel.id)).querySingle(); + .where(column(TestSyncableModel$Table.ID).is(testSyncableModel.id)).querySingle(); TestSyncableModel fromContentProvider = new TestSyncableModel(); fromContentProvider.id = testSyncableModel.id; diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/querymodel/QueryModelTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/querymodel/QueryModelTest.java index eca800eb9..37862dadc 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/querymodel/QueryModelTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/querymodel/QueryModelTest.java @@ -1,8 +1,5 @@ package com.raizlabs.android.dbflow.test.querymodel; -import android.database.Cursor; - -import com.raizlabs.android.dbflow.sql.language.ColumnAlias; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.sql.language.Where; @@ -11,6 +8,8 @@ import java.util.List; import java.util.UUID; +import static com.raizlabs.android.dbflow.sql.language.ColumnAlias.column; + /** * Description: Tests the {@link TestQueryModel} to ensure it works as expected. */ @@ -34,9 +33,9 @@ public void testSalaryModel() { salaryModel.department = "Developer"; salaryModel.save(); - Where selectQuery = new Select(ColumnAlias.column(SalaryModel$Table.DEPARTMENT), - ColumnAlias.column(SalaryModel$Table.SALARY).as("average_salary"), - ColumnAlias.column(SalaryModel$Table.NAME).as("newName")) + Where selectQuery = new Select(column(SalaryModel$Table.DEPARTMENT), + column(SalaryModel$Table.SALARY).as("average_salary"), + column(SalaryModel$Table.NAME).as("newName")) .from(SalaryModel.class).where().limit(1).groupBy(SalaryModel$Table.DEPARTMENT); TestQueryModel testQueryModel = selectQuery.queryCustomSingle(TestQueryModel.class); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/AsyncQueryTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/AsyncQueryTest.java index e05fab50d..a3c06bddd 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/AsyncQueryTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/AsyncQueryTest.java @@ -3,7 +3,6 @@ import android.database.Cursor; import com.raizlabs.android.dbflow.runtime.transaction.TransactionListenerAdapter; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.sql.language.Update; import com.raizlabs.android.dbflow.structure.AsyncModel; @@ -12,6 +11,8 @@ import com.raizlabs.android.dbflow.test.structure.TestModel1; import com.raizlabs.android.dbflow.test.structure.TestModel1$Table; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -23,7 +24,7 @@ public void testAsyncQuery() { testModel1.save(); new Select().from(TestModel1.class) - .where(Condition.column(TestModel1$Table.NAME).is("Async")) + .where(column(TestModel1$Table.NAME).is("Async")) .async().querySingle(new TransactionListenerAdapter() { @Override public void onResultReceived(TestModel1 testModel1) { @@ -32,8 +33,8 @@ public void onResultReceived(TestModel1 testModel1) { }); new Update<>(TestModel1.class) - .set(Condition.column(TestModel1$Table.NAME).is("Async2")) - .where(Condition.column(TestModel1$Table.NAME).is("Async")) + .set(column(TestModel1$Table.NAME).is("Async2")) + .where(column(TestModel1$Table.NAME).is("Async")) .async().query(new TransactionListenerAdapter() { @Override public void onResultReceived(Cursor cursor) { diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BlobModelTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BlobModelTest.java index e343e2f32..05d14136b 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BlobModelTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BlobModelTest.java @@ -1,10 +1,11 @@ package com.raizlabs.android.dbflow.test.sql; import com.raizlabs.android.dbflow.data.Blob; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.test.FlowTestCase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -21,7 +22,7 @@ public void testBlob() { assertTrue(blobModel.exists()); BlobModel model = new Select().from(BlobModel.class) - .where(Condition.column(BlobModel$Table.KEY) + .where(column(BlobModel$Table.KEY) .is(blobModel.key)) .querySingle(); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BoxedValueTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BoxedValueTest.java index 9216a2203..a36eec41c 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BoxedValueTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BoxedValueTest.java @@ -3,12 +3,13 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteException; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.test.FlowTestCase; import java.util.concurrent.atomic.AtomicLong; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: Test to ensure that passing null to non-null fields does not cause a NPE and that it * will fail. @@ -57,7 +58,7 @@ public void testBoxedValues_stringField() { private void loadModel() { testObject = new Select() .from(BoxedModel.class) - .where(Condition.column(BoxedModel$Table.ID).eq(testObject.id)) + .where(column(BoxedModel$Table.ID).eq(testObject.id)) .querySingle(); } diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BuilderTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BuilderTest.java index 6b5efedd2..0db9987b1 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BuilderTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/BuilderTest.java @@ -3,9 +3,11 @@ import com.raizlabs.android.dbflow.annotation.Collate; import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.builder.ConditionQueryBuilder; -import com.raizlabs.android.dbflow.sql.language.ColumnAlias; import com.raizlabs.android.dbflow.test.FlowTestCase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; +import static com.raizlabs.android.dbflow.sql.language.ColumnAlias.columnRaw; + /** * Author: andrewgrosner * Description: Test our {@link com.raizlabs.android.dbflow.sql.builder.ConditionQueryBuilder} and @@ -20,20 +22,20 @@ public void testConditions() { ConditionQueryBuilder conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class); conditionQueryBuilder.addConditions( - Condition.column("number").is(5L), - Condition.column("bytes").is(5), - Condition.column("fraction").is(6.5d)); + column("number").is(5L), + column("bytes").is(5), + column("fraction").is(6.5d)); assertEquals("`number`=5 AND `bytes`=5 AND `fraction`=6.5", conditionQueryBuilder.getQuery()); conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class) - .addCondition(Condition.column(ConditionModel$Table.NUMBER).between(5L).and(10L)); + .addCondition(column(ConditionModel$Table.NUMBER).between(5L).and(10L)); assertEquals("`number` BETWEEN 5 AND 10", conditionQueryBuilder.getQuery().trim()); } public void testCollate() { - Condition collate = Condition.column(ConditionModel$Table.NAME).is("James").collate(Collate.NOCASE); + Condition collate = column(ConditionModel$Table.NAME).is("James").collate(Collate.NOCASE); ConditionQueryBuilder conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class); collate.appendConditionToQuery(conditionQueryBuilder); @@ -43,25 +45,25 @@ public void testCollate() { public void testChainingConditions() { ConditionQueryBuilder conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class); - conditionQueryBuilder.addCondition(Condition.column(ConditionModel$Table.NAME).is("James").separator("OR")) - .addCondition(Condition.column(ConditionModel$Table.NUMBER).is(6).separator("AND")) - .addCondition(Condition.column(ConditionModel$Table.FRACTION).is(4.5d)); + conditionQueryBuilder.addCondition(column(ConditionModel$Table.NAME).is("James").separator("OR")) + .addCondition(column(ConditionModel$Table.NUMBER).is(6).separator("AND")) + .addCondition(column(ConditionModel$Table.FRACTION).is(4.5d)); assertEquals("`name`='James' OR `number`=6 AND `fraction`=4.5", conditionQueryBuilder.getQuery().trim()); } public void testIsOperators() { ConditionQueryBuilder conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class); - conditionQueryBuilder.addCondition(Condition.column(ConditionModel$Table.NAME).is("James")) - .or(Condition.column(ConditionModel$Table.FRACTION).isNotNull()); + conditionQueryBuilder.addCondition(column(ConditionModel$Table.NAME).is("James")) + .or(column(ConditionModel$Table.FRACTION).isNotNull()); assertEquals("`name`='James' OR `fraction` IS NOT NULL", conditionQueryBuilder.getQuery().trim()); } public void testInOperators() { - Condition.In in = Condition.column(ConditionModel$Table.NAME).in("Jason", "Ryan", "Michael"); + Condition.In in = column(ConditionModel$Table.NAME).in("Jason", "Ryan", "Michael"); ConditionQueryBuilder conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class, in); assertEquals("`name` IN ('Jason','Ryan','Michael')", conditionQueryBuilder.getQuery().trim()); - Condition.In notIn = Condition.column(ConditionModel$Table.NAME).notIn("Jason", "Ryan", "Michael"); + Condition.In notIn = column(ConditionModel$Table.NAME).notIn("Jason", "Ryan", "Michael"); conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class, notIn); assertEquals("`name` NOT IN ('Jason','Ryan','Michael')", conditionQueryBuilder.getQuery().trim()); } @@ -69,8 +71,8 @@ public void testInOperators() { public void testCombinedOperations() { Condition.CombinedCondition combinedCondition = Condition.CombinedCondition .begin(Condition.CombinedCondition - .begin(Condition.column(ColumnAlias.columnRaw("A"))).or(Condition.column(ColumnAlias.columnRaw("B")))) - .and(Condition.column(ColumnAlias.columnRaw("C"))); + .begin(column(columnRaw("A"))).or(column(columnRaw("B")))) + .and(column(columnRaw("C"))); ConditionQueryBuilder conditionQueryBuilder = new ConditionQueryBuilder<>(ConditionModel.class, combinedCondition); assertEquals("((A OR B) AND C)", conditionQueryBuilder.getQuery()); } diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/IndexTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/IndexTest.java index faf4eb6d7..52b61af89 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/IndexTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/IndexTest.java @@ -1,6 +1,5 @@ package com.raizlabs.android.dbflow.test.sql; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.index.Index; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; @@ -8,6 +7,8 @@ import java.util.List; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -37,7 +38,7 @@ public void testIndex() { List list = new Select().from(IndexModel.class) .indexedBy(modelIndex.getIndexName()) - .where(Condition.column(IndexModel$Table.SALARY).greaterThan(20000)).queryList(); + .where(column(IndexModel$Table.SALARY).greaterThan(20000)).queryList(); assertTrue(list.size() == 1); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/InsertTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/InsertTest.java index 64c768d07..d5c588317 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/InsertTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/InsertTest.java @@ -1,13 +1,14 @@ package com.raizlabs.android.dbflow.test.sql; import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Insert; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.test.FlowTestCase; import com.raizlabs.android.dbflow.test.TestDatabase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -26,7 +27,7 @@ public void testInsert() { FlowManager.getDatabase(TestDatabase.NAME).getWritableDatabase().execSQL(insert.getQuery()); InsertModel model = new Select().from(InsertModel.class) - .where(Condition.column(InsertModel$Table.NAME).is("Test")).querySingle(); + .where(column(InsertModel$Table.NAME).is("Test")).querySingle(); assertNotNull(model); @@ -37,7 +38,7 @@ public void testInsert() { FlowManager.getDatabase(TestDatabase.NAME).getWritableDatabase().execSQL(insert.getQuery()); model = new Select().from(InsertModel.class) - .where(Condition.column(InsertModel$Table.NAME).is("Test2")).querySingle(); + .where(column(InsertModel$Table.NAME).is("Test2")).querySingle(); assertNotNull(model); } } diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/MigrationTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/MigrationTest.java index d4349b121..db3b4655c 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/MigrationTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/MigrationTest.java @@ -6,7 +6,6 @@ import com.raizlabs.android.dbflow.config.FlowLog; import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.sql.migration.AlterTableMigration; import com.raizlabs.android.dbflow.sql.migration.IndexMigration; @@ -15,6 +14,8 @@ import java.util.Arrays; import java.util.List; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -77,7 +78,7 @@ public void testMigration() { public void testUpdateMigration() { UpdateTableMigration updateTableMigration = new UpdateTableMigration<>(MigrationModel.class) - .set(Condition.column("name").is("test")).where(Condition.column("name").is("notTest")); + .set(column("name").is("test")).where(column("name").is("notTest")); updateTableMigration.onPreMigrate(); assertEquals("UPDATE `MigrationModel` SET `name`='test' WHERE `name`='notTest'", updateTableMigration.getQuery().trim()); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java index ba834b5c2..0975a9c21 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SelectTest.java @@ -4,12 +4,14 @@ import com.raizlabs.android.dbflow.sql.language.Join; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.sql.language.Where; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.test.FlowTestCase; import com.raizlabs.android.dbflow.test.structure.TestModel1; import java.util.List; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; +import static com.raizlabs.android.dbflow.sql.language.OrderBy.columns; + /** * Description: */ @@ -17,14 +19,14 @@ public class SelectTest extends FlowTestCase { public void testSelectStatement() { Where where = new Select("name").from(TestModel1.class) - .where(Condition.column("name").is("test")); + .where(column("name").is("test")); assertEquals("SELECT `name` FROM `TestModel1` WHERE `name`='test'", where.getQuery().trim()); where.query(); Where where1 = new Select("name", "type").from(TestModel3.class) - .where(Condition.column("name").is("test"), - Condition.column("type").is("test")); + .where(column("name").is("test"), + column("type").is("test")); assertEquals("SELECT `name`, `type` FROM `TestModel3` WHERE `name`='test' AND `type`='test'", where1.getQuery().trim()); @@ -41,7 +43,7 @@ public void testSelectStatement() { Where where4 = new Select().from(TestModel3.class) .where("`name`=?", "test") - .and(Condition.column(TestModel3$Table.TYPE).is("test")); + .and(column(TestModel3$Table.TYPE).is("test")); assertEquals("SELECT * FROM `TestModel3` WHERE `name`='test' AND `type`='test'", where4.getQuery().trim()); @@ -49,6 +51,11 @@ public void testSelectStatement() { .byIds("Test"); assertEquals("SELECT * FROM `TestModel3` WHERE `name`='Test'", where5.getQuery().trim()); + + Where where6 = new Select().method("date", "type") + .from(TestModel3.class) + .orderBy(columns("type", "name").ascending()); + assertEquals("SELECT date(`type`) FROM `TestModel3` ORDER BY `type`, `name` ASC", where6.getQuery().trim()); } public void testJoins() { @@ -62,7 +69,7 @@ public void testJoins() { testModel2.save(); From baseFrom = new Select().from(TestModel1.class); - baseFrom.join(TestModel3.class, Join.JoinType.CROSS).on(Condition.column("TestModel1.name").is("TestModel3.name")); + baseFrom.join(TestModel3.class, Join.JoinType.CROSS).on(column("TestModel1.name").is("TestModel3.name")); assertEquals("SELECT * FROM `TestModel1` CROSS JOIN `TestModel3` ON `TestModel1`.`name`=TestModel3.name", baseFrom.getQuery().trim()); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SubqueryTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SubqueryTest.java index 41f460b02..103488638 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SubqueryTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/SubqueryTest.java @@ -1,9 +1,11 @@ package com.raizlabs.android.dbflow.test.sql; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.test.FlowTestCase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; +import static com.raizlabs.android.dbflow.sql.builder.Condition.columnRaw; + /** * Description: Validates subquery formatting */ @@ -15,7 +17,7 @@ public void testSubquery() { String query = new Select() .from(BoxedModel.class) .where().exists(new Select().from(BoxedModel.class) - .where(Condition.columnRaw(BoxedModel$Table.INTEGERFIELD) + .where(columnRaw(BoxedModel$Table.INTEGERFIELD) .eq(BoxedModel$Table.INTEGERFIELDNOTNULL))) .getQuery(); @@ -25,9 +27,9 @@ public void testSubquery() { query = new Select() .from(BoxedModel.class) - .where(Condition.column(BoxedModel$Table.INTEGERFIELD) + .where(column(BoxedModel$Table.INTEGERFIELD) .greaterThan(new Select().avg(BoxedModel$Table.INTEGERFIELD).from(BoxedModel.class) - .where(Condition.columnRaw(BoxedModel$Table.INTEGERFIELD) + .where(columnRaw(BoxedModel$Table.INTEGERFIELD) .eq("BoxedModel." + BoxedModel$Table.INTEGERFIELD)))) .getQuery(); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TriggerTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TriggerTest.java index 9f2824f59..4d92f4b48 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TriggerTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/sql/TriggerTest.java @@ -1,6 +1,5 @@ package com.raizlabs.android.dbflow.test.sql; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.sql.language.Update; @@ -11,6 +10,8 @@ import com.raizlabs.android.dbflow.test.structure.TestModel1; import com.raizlabs.android.dbflow.test.structure.TestModel1$Table; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -19,8 +20,8 @@ public class TriggerTest extends FlowTestCase { public void testTriggerLanguage() { Where logic = new Update<>(TestModel1.class) - .set(Condition.column(TestModel1$Table.NAME).is("Jason")) - .where(Condition.column(TestModel1$Table.NAME).is("Jason2")); + .set(column(TestModel1$Table.NAME).is("Jason")) + .where(column(TestModel1$Table.NAME).is("Jason2")); String trigger = Trigger.create("MyTrigger") .after().insert(ConditionModel.class).begin(logic).getQuery(); assertEquals("CREATE TRIGGER IF NOT EXISTS `MyTrigger` AFTER INSERT ON `ConditionModel` " + @@ -41,7 +42,7 @@ public void testTriggerFunctions() { CompletedTrigger trigger = Trigger.create("TestTrigger") .after().insert(ConditionModel.class).begin(new Update<>(TestUpdateModel.class) - .set(Condition.column(TestUpdateModel$Table.VALUE).is("Fired"))); + .set(column(TestUpdateModel$Table.VALUE).is("Fired"))); TestUpdateModel model = new TestUpdateModel(); model.name = "Test"; @@ -56,7 +57,7 @@ public void testTriggerFunctions() { conditionModel.insert(); model = new Select().from(TestUpdateModel.class) - .where(Condition.column(TestUpdateModel$Table.NAME).is("Test")).querySingle(); + .where(column(TestUpdateModel$Table.NAME).is("Test")).querySingle(); assertEquals(model.value, "Fired"); trigger.disable(); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ForeignKeyTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ForeignKeyTest.java index 700252e4a..4d327c394 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ForeignKeyTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ForeignKeyTest.java @@ -1,10 +1,11 @@ package com.raizlabs.android.dbflow.test.structure; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.test.FlowTestCase; import com.raizlabs.android.dbflow.test.structure.autoincrement.TestModelAI; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -23,7 +24,7 @@ public void testForeignKey() { foreignModel.save(); ForeignModel retrieved = new Select().from(ForeignModel.class) - .where(Condition.column(ForeignModel$Table.NAME).is("Test")) + .where(column(ForeignModel$Table.NAME).is("Test")) .querySingle(); assertNotNull(retrieved); assertNotNull(retrieved.testModel1); @@ -42,7 +43,7 @@ public void testForeignKey2() { foreignModel2.save(); ForeignModel2 retrieved = new Select().from(ForeignModel2.class) - .where(Condition.column(ForeignModel2$Table.NAME).is("Test")) + .where(column(ForeignModel2$Table.NAME).is("Test")) .querySingle(); assertNotNull(retrieved); assertNotNull(retrieved.testModelAI); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ListenerModelTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ListenerModelTest.java index 61f718f27..94a5d0994 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ListenerModelTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/ListenerModelTest.java @@ -5,7 +5,6 @@ import android.database.sqlite.SQLiteStatement; import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.structure.ModelAdapter; @@ -14,6 +13,8 @@ import com.raizlabs.android.dbflow.structure.listener.SQLiteStatementListener; import com.raizlabs.android.dbflow.test.FlowTestCase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -53,7 +54,7 @@ public void onLoadFromCursor(Cursor cursor) { listenerModel.update(); ModelAdapter modelModelAdapter = FlowManager.getModelAdapter(ListenerModel.class); - Cursor cursor = new Select().from(ListenerModel.class).where(Condition.column(ListenerModel$Table.NAME).is("This is a test")).query(); + Cursor cursor = new Select().from(ListenerModel.class).where(column(ListenerModel$Table.NAME).is("This is a test")).query(); assertNotNull(cursor); assertTrue(cursor.moveToFirst()); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/autoincrement/ModelAutoIncrementTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/autoincrement/ModelAutoIncrementTest.java index df9025d01..91f0653a7 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/autoincrement/ModelAutoIncrementTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/autoincrement/ModelAutoIncrementTest.java @@ -1,11 +1,12 @@ package com.raizlabs.android.dbflow.test.structure.autoincrement; import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.test.FlowTestCase; import com.raizlabs.android.dbflow.test.TestDatabase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -25,7 +26,7 @@ public void testModelAutoIncrement() { testModelAI2.update(); TestModelAI testModelAI3 = new Select().from(TestModelAI.class) - .where(Condition.column(TestModelAI$Table.ID).is(testModelAI.id)) + .where(column(TestModelAI$Table.ID).is(testModelAI.id)) .querySingle(); assertEquals(testModelAI3.name, testModelAI2.name); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/caching/CacheableModelTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/caching/CacheableModelTest.java index c52fa90a6..d368358c6 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/caching/CacheableModelTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/structure/caching/CacheableModelTest.java @@ -1,12 +1,13 @@ package com.raizlabs.android.dbflow.test.structure.caching; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.structure.cache.BaseCacheableModel; import com.raizlabs.android.dbflow.structure.cache.ModelCache; import com.raizlabs.android.dbflow.test.FlowTestCase; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -29,7 +30,7 @@ public void testCacheableModel() { assertNotNull(cacheableModel); assertEquals(new Select().from(CacheableModel.class). - where(Condition.column(CacheableModel$Table.ID).is(id)) + where(column(CacheableModel$Table.ID).is(id)) .querySingle(), cacheableModel); model.delete(); @@ -54,7 +55,7 @@ public void testCacheableModel2() { assertNotNull(cacheableModel); assertEquals(new Select().from(CacheableModel2.class) - .where(Condition.column(CacheableModel2$Table.ID).is(id)) + .where(column(CacheableModel2$Table.ID).is(id)) .querySingle(), cacheableModel); model.delete(); @@ -80,7 +81,7 @@ public void testCacheableModel3() { assertNotNull(cacheableModel); assertEquals(new Select().from(CacheableModel3.class) - .where(Condition.column(CacheableModel3$Table.CACHE_ID).is(id)) + .where(column(CacheableModel3$Table.CACHE_ID).is(id)) .querySingle(), cacheableModel); cacheableModel3.delete(); diff --git a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/typeconverter/TypeConverterTest.java b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/typeconverter/TypeConverterTest.java index 579b30b1a..6728297b6 100644 --- a/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/typeconverter/TypeConverterTest.java +++ b/DBFlow/src/androidTest/java/com/raizlabs/android/dbflow/test/typeconverter/TypeConverterTest.java @@ -2,7 +2,6 @@ import android.location.Location; -import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.sql.language.Select; import com.raizlabs.android.dbflow.sql.language.Update; @@ -14,6 +13,8 @@ import java.util.Calendar; import java.util.Date; +import static com.raizlabs.android.dbflow.sql.builder.Condition.column; + /** * Description: */ @@ -21,7 +22,7 @@ public class TypeConverterTest extends FlowTestCase { public void testConverters() { - new Delete().from(TestType.class).where().query(); + Delete.table(TestType.class); TestType testType = new TestType(); testType.name = "Name"; @@ -56,7 +57,7 @@ public void testConverters() { testType.save(); TestType retrieved = new Select().from(TestType.class) - .where(Condition.column(TestType$Table.NAME).is("Name")) + .where(column(TestType$Table.NAME).is("Name")) .querySingle(); assertNotNull(retrieved); @@ -90,7 +91,7 @@ public void testConvertersNullValues() { testType.save(); TestType retrieved = new Select().from(TestType.class) - .where(Condition.column(TestType$Table.NAME).is("Name")) + .where(column(TestType$Table.NAME).is("Name")) .querySingle(); assertNotNull(retrieved); @@ -121,11 +122,11 @@ public void testConvertersNullDatabaseConversionValues() { */ new Update<>(TestType.class) .set(TestType$Table.NATIVEBOOLEAN + " = null") - .where(Condition.column(TestType$Table.NAME).eq(testType.name)) + .where(column(TestType$Table.NAME).eq(testType.name)) .queryClose(); TestType retrieved = new Select().from(TestType.class) - .where(Condition.column(TestType$Table.NAME).is("Name")) + .where(column(TestType$Table.NAME).is("Name")) .querySingle(); assertNotNull(retrieved); diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java index f5feb454f..36111383c 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java @@ -7,6 +7,7 @@ import com.raizlabs.android.dbflow.list.FlowQueryList; import com.raizlabs.android.dbflow.sql.Query; import com.raizlabs.android.dbflow.sql.QueryBuilder; +import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.builder.ConditionQueryBuilder; import com.raizlabs.android.dbflow.sql.builder.SQLCondition; import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; @@ -17,7 +18,7 @@ import java.util.List; /** - * Description: The SQL FROM query wrapper that must have a {@link com.raizlabs.android.dbflow.sql.Query} base. + * Description: The SQL FROM query wrapper that must have a {@link Query} base. */ public class From extends BaseModelQueriable implements WhereBase, ModelQueriable { @@ -107,7 +108,7 @@ public Where where() { /** * @param conditionQueryBuilder The builder of a specific set of conditions used in this query - * @return A {@link Where} statement with the specified {@link com.raizlabs.android.dbflow.sql.builder.ConditionQueryBuilder}. + * @return A {@link Where} statement with the specified {@link ConditionQueryBuilder}. */ public Where where(ConditionQueryBuilder conditionQueryBuilder) { return where().whereQuery(conditionQueryBuilder); @@ -115,7 +116,7 @@ public Where where(ConditionQueryBuilder conditionQueryB /** * @param conditions The array of conditions that define this WHERE statement - * @return A {@link Where} statement with the specified array of {@link com.raizlabs.android.dbflow.sql.builder.Condition}. + * @return A {@link Where} statement with the specified array of {@link Condition}. */ public Where where(SQLCondition... conditions) { return where().andThese(conditions); @@ -237,9 +238,9 @@ public String getQuery() { } /** - * @return The base query, usually a {@link com.raizlabs.android.dbflow.sql.language.Delete}. - * {@link com.raizlabs.android.dbflow.sql.language.Select}, or {@link com.raizlabs.android.dbflow.sql.language.Update} + * @return The base query, usually a {@link Delete}, {@link Select}, or {@link Update} */ + @Override public Query getQueryBuilderBase() { return queryBase; } From 88fd6936ab0e318ff23475355007db01b304996e Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 22:12:53 -0400 Subject: [PATCH 23/24] added a static method to Update --- .../android/dbflow/sql/language/Update.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Update.java b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Update.java index 2d7301b30..5ffcdcb61 100644 --- a/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Update.java +++ b/DBFlow/src/main/java/com/raizlabs/android/dbflow/sql/language/Update.java @@ -20,7 +20,20 @@ public class Update implements Query { private final Class mTable; + /** + * @param table The table to update. + * @param The class that implements {@link Model} + * @return A new update object. Begins a generic UPDATE query. + */ + public static Update table(Class table) { + return new Update(table); + } + /** + * Constructs new instace of an UPDATE query with the specified table. + * + * @param table The table to use. + */ public Update(Class table) { mTable = table; } From bdea2447cf751f05f0d6f5035a5e7fde2fbfb908 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Thu, 2 Jul 2015 22:20:33 -0400 Subject: [PATCH 24/24] removed other versions from readme --- README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/README.md b/README.md index 9c8af234f..375628e29 100644 --- a/README.md +++ b/README.md @@ -51,23 +51,6 @@ looping through saving models in a `ProcessModelTransaction`. 10. Escalated `convertToCacheableList()` to `public` and now can query to know if a `Model` has a valid caching id. Also some more public methods added to `SqlUtils`! - -#### 2.1.0 - -1. Library now is on jCenter()/bintray! -2. Full Enum Support. Note: they must be standard columns. We do not support foreign key or primary key for enums. -3. Can now define inherited properties to use as columns. Note: They can only be normal, accessible to the subclass columns for now. Just - define `@Table(inheritedColumns = {@InheritedColumn(column = @Column, fieldName = "fieldName")}`. -4. Bug Fixes, readme enhancements, Logging Improvements, and improved code commenting -5. Function support for SQLite methods in a condition such that `date(myColumn1)=1433872730` can be written - as `Condition.columnsWithFunction("date", "myColumn1").eq(1433872730)` -6. Fixed an issue where `Condition` instead of `SQLCondition` were leftover as a param in a few methods. - -#### 2.0.0 - -1. Massive, massive changes to the library. -2. For all changes, check out the migration guide [here](https://github.com/Raizlabs/DBFlow/blob/master/usage/Migration2Guide.md) - for older changes, from other xx.xx versions, check it out [here](https://github.com/Raizlabs/DBFlow/wiki) ## Usage Docs