Remove DatabaseDao

pull/618/head
Alex Baker 7 years ago
parent c830b7f7ec
commit 6d027d01ed

@ -1,172 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.data;
import android.content.ContentValues;
import android.database.Cursor;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.UUIDHelper;
import org.tasks.BuildConfig;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import timber.log.Timber;
/**
* DAO for reading data from an instance of {@link Database}. If you
* are writing an add-on for Astrid, you probably want to be using a subclass
* of ContentResolverDao instead.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class DatabaseDao {
private final Table table = Task.TABLE;
private final Database database;
public DatabaseDao(Database database) {
this.database = database;
}
public List<Task> toList(Query query) {
return query(query).toList();
}
/**
* Construct a query with SQL DSL objects
*/
public TodorooCursor query(Query query) {
query.from(table);
String queryString = query.toString();
if (BuildConfig.DEBUG) {
Timber.v(queryString);
}
Cursor cursor = database.rawQuery(queryString);
return new TodorooCursor(cursor, query.getFields());
}
/**
* Delete the given id
* @return true if delete was successful
*/
public boolean delete(long id) {
return database.delete(table.name,
AbstractModel.ID_PROPERTY.eq(id).toString(), null) > 0;
}
/**
* Delete all matching a clause
* @param where predicate for deletion
* @return # of deleted items
*/
public int deleteWhere(Criterion where) {
Timber.v("deleteWhere(%s)", where);
return database.delete(table.name,
where.toString(), null);
}
/**
* Update all matching a clause to have the values set on template object.
* <p>
* Example (updates "joe" => "bob" in metadata value1):
* {code}
* Metadata item = new Metadata();
* item.setVALUE1("bob");
* update(item, Metadata.VALUE1.eq("joe"));
* {code}
* @param where sql criteria
* @param template set fields on this object in order to set them in the db.
* @return # of updated items
*/
public int update(Criterion where, Task template) {
return database.update(table.name, template.getSetValues(),
where.toString());
}
private interface DatabaseChangeOp {
boolean makeChange();
}
private boolean insertOrUpdateAndRecordChanges(Task item, DatabaseChangeOp op) {
final AtomicBoolean result = new AtomicBoolean(false);
synchronized(database) {
result.set(op.makeChange());
if (result.get()) {
item.markSaved();
if (BuildConfig.DEBUG) {
Timber.v("%s %s", op, item.toString());
}
}
}
return result.get();
}
/**
* Creates the given item.
* @param item
* item model
* @return returns true on success.
*/
public boolean createNew(final Task item) {
if (!item.containsValue(Task.UUID) || Task.isUuidEmpty(item.getUuid())) {
item.setUuid(UUIDHelper.newUUID());
}
DatabaseChangeOp insert = new DatabaseChangeOp() {
@Override
public boolean makeChange() {
ContentValues mergedValues = item.getMergedValues();
mergedValues.remove(AbstractModel.ID_PROPERTY.name);
long newRow = database.insert(table.name, mergedValues);
boolean result = newRow >= 0;
if (result) {
item.setId(newRow);
}
return result;
}
@Override
public String toString() {
return "INSERT";
}
};
return insertOrUpdateAndRecordChanges(item, insert);
}
/**
* Saves the given item. Will not create a new item!
* @param item
* item model
* @return returns true on success.
*/
public boolean saveExisting(final Task item) {
final ContentValues values = item.getSetValues();
if(values == null || values.size() == 0) // nothing changed
{
return true;
}
DatabaseChangeOp update = new DatabaseChangeOp() {
@Override
public boolean makeChange() {
return database.update(table.name, values,
AbstractModel.ID_PROPERTY.eq(item.getId()).toString()) > 0;
}
@Override
public String toString() {
return "UPDATE";
}
};
return insertOrUpdateAndRecordChanges(item, update);
}
}

@ -70,13 +70,12 @@ public class OldTaskPreferences extends InjectingPreferenceActivity {
.setPositiveButton(android.R.string.ok, (dialog, which) -> new ProgressDialogAsyncTask(OldTaskPreferences.this, dialogBuilder) { .setPositiveButton(android.R.string.ok, (dialog, which) -> new ProgressDialogAsyncTask(OldTaskPreferences.this, dialogBuilder) {
@Override @Override
protected Integer doInBackground(Void... params) { protected Integer doInBackground(Void... params) {
Query query = Query.select(Task.ID, Task.CALENDAR_URI) List<Task> deleted = taskDao.getDeleted();
.where(Criterion.and(Task.DELETION_DATE.gt(0))); for (Task task : deleted) {
for (Task task : taskDao.toList(query)) {
calendarEventProvider.deleteEvent(task); calendarEventProvider.deleteEvent(task);
taskDao.delete(task.getId()); taskDao.delete(task.getId());
} }
return taskDao.deleteWhere(Task.DELETION_DATE.gt(0)); return deleted.size();
} }
@Override @Override
@ -94,7 +93,9 @@ public class OldTaskPreferences extends InjectingPreferenceActivity {
@Override @Override
protected Integer doInBackground(Void... params) { protected Integer doInBackground(Void... params) {
return deleteCalendarEvents(Criterion.and(Task.COMPLETION_DATE.gt(0), Task.CALENDAR_URI.isNotNull())); int result = deleteCalendarEvents(Criterion.and(Task.COMPLETION_DATE.gt(0), Task.CALENDAR_URI.isNotNull()));
taskDao.clearCompletedCalendarEvents();
return result;
} }
@Override @Override
@ -111,7 +112,9 @@ public class OldTaskPreferences extends InjectingPreferenceActivity {
.setPositiveButton(android.R.string.ok, (dialog, which) -> new ProgressDialogAsyncTask(OldTaskPreferences.this, dialogBuilder) { .setPositiveButton(android.R.string.ok, (dialog, which) -> new ProgressDialogAsyncTask(OldTaskPreferences.this, dialogBuilder) {
@Override @Override
protected Integer doInBackground(Void... params) { protected Integer doInBackground(Void... params) {
return deleteCalendarEvents(Task.CALENDAR_URI.isNotNull()); int result = deleteCalendarEvents(Task.CALENDAR_URI.isNotNull());
taskDao.clearAllCalendarEvents();
return result;
} }
@Override @Override
@ -131,11 +134,6 @@ public class OldTaskPreferences extends InjectingPreferenceActivity {
deletedEventCount++; deletedEventCount++;
} }
} }
// mass update the CALENDAR_URI here,
// since the GCalHelper doesnt save it due to performance-reasons
Task template = new Task();
template.setCalendarUri(""); //$NON-NLS-1$
taskDao.update(criterion, template);
return deletedEventCount; return deletedEventCount;
} }

@ -80,6 +80,8 @@ public abstract class Database extends RoomDatabase {
public static final String NAME = "database"; public static final String NAME = "database";
private static final String TABLE_NAME = Task.TABLE.name;
private SupportSQLiteDatabase database; private SupportSQLiteDatabase database;
private Runnable onDatabaseUpdated; private Runnable onDatabaseUpdated;
@ -175,10 +177,10 @@ public abstract class Database extends RoomDatabase {
return getDatabase().query(sql, null); return getDatabase().query(sql, null);
} }
public long insert(String table, ContentValues values) { public long insert(ContentValues values) {
long result; long result;
try { try {
result = getDatabase().insert(table, SQLiteDatabase.CONFLICT_REPLACE, values); result = getDatabase().insert(TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values);
} catch (SQLiteConstraintException e) { // Throw these exceptions } catch (SQLiteConstraintException e) { // Throw these exceptions
throw e; throw e;
} catch (Exception e) { // Suppress others } catch (Exception e) { // Suppress others
@ -189,14 +191,8 @@ public abstract class Database extends RoomDatabase {
return result; return result;
} }
public int delete(String table, String whereClause, String[] whereArgs) { public int update(ContentValues values, String whereClause) {
int result = getDatabase().delete(table, whereClause, whereArgs); int result = getDatabase().update(TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, whereClause, null);
onDatabaseUpdated();
return result;
}
public int update(String table, ContentValues values, String whereClause) {
int result = getDatabase().update(table, SQLiteDatabase.CONFLICT_REPLACE, values, whereClause, null);
onDatabaseUpdated(); onDatabaseUpdated();
return result; return result;
} }

@ -10,7 +10,7 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import com.todoroo.andlib.data.DatabaseDao; import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
@ -22,7 +22,9 @@ import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskApiDao; import com.todoroo.astrid.data.TaskApiDao;
import com.todoroo.astrid.helper.UUIDHelper;
import org.tasks.BuildConfig;
import org.tasks.LocalBroadcastManager; import org.tasks.LocalBroadcastManager;
import org.tasks.R; import org.tasks.R;
import org.tasks.data.AlarmDao; import org.tasks.data.AlarmDao;
@ -34,6 +36,9 @@ import org.tasks.preferences.Preferences;
import org.tasks.receivers.PushReceiver; import org.tasks.receivers.PushReceiver;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import timber.log.Timber;
/** /**
* Data Access layer for {@link Task}-related operations. * Data Access layer for {@link Task}-related operations.
@ -46,7 +51,7 @@ public abstract class TaskDao {
public static final String TRANS_SUPPRESS_REFRESH = "suppress-refresh"; public static final String TRANS_SUPPRESS_REFRESH = "suppress-refresh";
private final DatabaseDao dao; private final Database database;
private LocalBroadcastManager localBroadcastManager; private LocalBroadcastManager localBroadcastManager;
private Preferences preferences; private Preferences preferences;
@ -57,7 +62,7 @@ public abstract class TaskDao {
private Context context; private Context context;
public TaskDao(Database database) { public TaskDao(Database database) {
dao = new DatabaseDao(database); this.database = database;
} }
public void initialize(Context context, Preferences preferences, LocalBroadcastManager localBroadcastManager, public void initialize(Context context, Preferences preferences, LocalBroadcastManager localBroadcastManager,
@ -71,12 +76,8 @@ public abstract class TaskDao {
this.googleTaskDao = googleTaskDao; this.googleTaskDao = googleTaskDao;
} }
public TodorooCursor query(Query query) {
return dao.query(query);
}
public List<Task> selectActive(Criterion criterion) { public List<Task> selectActive(Criterion criterion) {
return dao.toList(Query.select(Task.PROPERTIES).where(Criterion.and(TaskCriteria.isActive(), criterion))); return query(Query.select(Task.PROPERTIES).where(Criterion.and(TaskCriteria.isActive(), criterion))).toList();
} }
@android.arch.persistence.room.Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1") @android.arch.persistence.room.Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
@ -88,7 +89,7 @@ public abstract class TaskDao {
} }
public int count(Query query) { public int count(Query query) {
Cursor cursor = dao.query(query); Cursor cursor = query(query);
try { try {
return cursor.getCount(); return cursor.getCount();
} finally { } finally {
@ -98,34 +99,17 @@ public abstract class TaskDao {
public List<Task> query(Filter filter) { public List<Task> query(Filter filter) {
String query = PermaSql.replacePlaceholders(filter.getSqlQuery()); String query = PermaSql.replacePlaceholders(filter.getSqlQuery());
return dao.toList(Query.select(Task.PROPERTIES).withQueryTemplate(query)); return query(Query.select(Task.PROPERTIES).withQueryTemplate(query)).toList();
}
/**
* Update all matching a clause to have the values set on template object.
* <p>
* Example (updates "joe" => "bob" in metadata value1):
* {code}
* Metadata item = new Metadata();
* item.setVALUE1("bob");
* update(item, Metadata.VALUE1.eq("joe"));
* {code}
* @param where sql criteria
* @param template set fields on this object in order to set them in the db.
* @return # of updated items
*/
public int update(Criterion where, Task template) {
return dao.update(where, template);
}
public int deleteWhere(Criterion criterion) {
return dao.deleteWhere(criterion);
} }
public List<Task> toList(Query query) { public List<Task> toList(Query query) {
return dao.toList(query); return query(query).toList();
} }
@android.arch.persistence.room.Query("UPDATE tasks SET completed = :completionDate " +
"WHERE remoteId = :remoteId")
public abstract void setCompletionDate(String remoteId, long completionDate);
// --- SQL clause generators // --- SQL clause generators
/** /**
@ -169,6 +153,20 @@ public abstract class TaskDao {
@android.arch.persistence.room.Query("SELECT remoteId FROM tasks WHERE _id = :localId") @android.arch.persistence.room.Query("SELECT remoteId FROM tasks WHERE _id = :localId")
public abstract String uuidFromLocalId(long localId); public abstract String uuidFromLocalId(long localId);
@android.arch.persistence.room.Query("UPDATE tasks SET calendarUri = '' " +
"WHERE calendarUri NOT NULL AND calendarUri != ''")
public abstract void clearAllCalendarEvents();
@android.arch.persistence.room.Query("UPDATE tasks SET calendarUri = '' " +
"WHERE completed > 0 AND calendarUri NOT NULL AND calendarUri != ''")
public abstract void clearCompletedCalendarEvents();
@android.arch.persistence.room.Query("SELECT * FROM tasks WHERE deleted > 0")
public abstract List<Task> getDeleted();
@android.arch.persistence.room.Query("DELETE FROM tasks WHERE _id = :id")
abstract int deleteById(long id);
// --- delete // --- delete
/** /**
@ -177,7 +175,7 @@ public abstract class TaskDao {
* @return true if delete was successful * @return true if delete was successful
*/ */
public boolean delete(long id) { public boolean delete(long id) {
boolean result = dao.delete(id); boolean result = deleteById(id) > 0;
if(!result) { if(!result) {
return false; return false;
} }
@ -236,7 +234,30 @@ public abstract class TaskDao {
setDefaultReminders(preferences, item); setDefaultReminders(preferences, item);
ContentValues values = item.getSetValues(); ContentValues values = item.getSetValues();
if(dao.createNew(item)) {
if (!item.containsValue(Task.UUID) || Task.isUuidEmpty(item.getUuid())) {
item.setUuid(UUIDHelper.newUUID());
}
DatabaseChangeOp insert = new DatabaseChangeOp() {
@Override
public boolean makeChange() {
ContentValues mergedValues = item.getMergedValues();
mergedValues.remove(AbstractModel.ID_PROPERTY.name);
long newRow = database.insert(mergedValues);
boolean result = newRow >= 0;
if (result) {
item.setId(newRow);
}
return result;
}
@Override
public String toString() {
return "INSERT";
}
};
if (insertOrUpdateAndRecordChanges(item, insert)) {
return values; return values;
} }
return null; return null;
@ -274,7 +295,19 @@ public abstract class TaskDao {
item.setModificationDate(DateUtilities.now()); item.setModificationDate(DateUtilities.now());
} }
} }
if(dao.saveExisting(item)) { DatabaseChangeOp update = new DatabaseChangeOp() {
@Override
public boolean makeChange() {
return database.update(values,
AbstractModel.ID_PROPERTY.eq(item.getId()).toString()) > 0;
}
@Override
public String toString() {
return "UPDATE";
}
};
if (insertOrUpdateAndRecordChanges(item, update)) {
return values; return values;
} }
return null; return null;
@ -298,5 +331,36 @@ public abstract class TaskDao {
? Query.selectDistinct(properties) ? Query.selectDistinct(properties)
: Query.select(properties).withQueryTemplate(PermaSql.replacePlaceholders(queryTemplate))); : Query.select(properties).withQueryTemplate(PermaSql.replacePlaceholders(queryTemplate)));
} }
/**
* Construct a query with SQL DSL objects
*/
public TodorooCursor query(Query query) {
query.from(Task.TABLE);
String queryString = query.toString();
if (BuildConfig.DEBUG) {
Timber.v(queryString);
}
Cursor cursor = database.rawQuery(queryString);
return new TodorooCursor(cursor, query.getFields());
}
private interface DatabaseChangeOp {
boolean makeChange();
}
private boolean insertOrUpdateAndRecordChanges(Task item, DatabaseChangeOp op) {
final AtomicBoolean result = new AtomicBoolean(false);
synchronized(database) {
result.set(op.makeChange());
if (result.get()) {
item.markSaved();
if (BuildConfig.DEBUG) {
Timber.v("%s %s", op, item.toString());
}
}
}
return result.get();
}
} }

@ -130,9 +130,7 @@ class AstridOrderedListFragmentHelper {
ArrayList<String> chained = chainedCompletions.get(itemId); ArrayList<String> chained = chainedCompletions.get(itemId);
if(chained != null) { if(chained != null) {
for(String taskId : chained) { for(String taskId : chained) {
Task model = new Task(); taskDao.setCompletionDate(taskId, completionDate);
model.setCompletionDate(completionDate);
taskDao.update(Task.UUID.eq(taskId), model);
} }
taskAdapter.notifyDataSetInvalidated(); taskAdapter.notifyDataSetInvalidated();
} }
@ -142,9 +140,7 @@ class AstridOrderedListFragmentHelper {
final ArrayList<String> chained = new ArrayList<>(); final ArrayList<String> chained = new ArrayList<>();
updater.applyToDescendants(itemId, node -> { updater.applyToDescendants(itemId, node -> {
String uuid = node.uuid; String uuid = node.uuid;
Task model = new Task(); taskDao.setCompletionDate(uuid, completionDate);
model.setCompletionDate(completionDate);
taskDao.update(Task.UUID.eq(uuid), model);
chained.add(node.uuid); chained.add(node.uuid);
}); });

Loading…
Cancel
Save