/**
* Copyright (C) 2013 Johannes Schnatterer
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This file is part of nusic.
*
* nusic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nusic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nusic. If not, see <http://www.gnu.org/licenses/>.
*/
package info.schnatterer.nusic.data.dao.sqlite;
import info.schnatterer.nusic.data.DatabaseException;
import info.schnatterer.nusic.data.dao.GenericDao;
import info.schnatterer.nusic.data.model.Entity;
import info.schnatterer.nusic.data.util.SqliteUtil;
import java.util.Map;
import android.content.AsyncTaskLoader;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.CancellationSignal;
import android.provider.BaseColumns;
/**
* Wraps the {@link SQLiteDatabase} object as well as the {@link Cursor} and
* provides {@link SQLiteDatabase}'s <code>query()</code> methods to its
* concrete sub classes.
*
* Allows for only one concurrent database query at a time, which in turn allows
* other classes (such as {@link AsyncTaskLoader}s to close the {@link Cursor}
* any time by calling {@link #closeCursor()}.
*
* @author schnatterer
*
* @param <T>
*/
public abstract class AbstractSqliteDao<T extends Entity> implements
GenericDao<T> {
private SQLiteDatabase db;
private Cursor cursor = null;
private Context context;
public AbstractSqliteDao(Context context, SQLiteDatabase db) {
this.context = context;
// Opens database connection
this.db = db;
}
/**
* Makes sure the cursor is closed.
*/
public void closeCursor() {
// Close the cursor
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
cursor = null;
}
/**
* For reasons of efficiency when joining.
*
* @param cursor
* @param startIndex
* @return
*/
public abstract Long toId(Cursor cursor, int startIndex);
/**
* Only fills in "direct" columns. Relation (joins) must be taken care of by
* DAO's methods.
*
* @param cursor
* @param startIndex
* @return
*/
public abstract T toEntity(Cursor cursor, int startIndex);
public abstract ContentValues toContentValues(T t);
public abstract String getTableName();
protected abstract Long getId(T entity);
@Override
public long save(T entity) throws DatabaseException {
try {
long id = db.insertOrThrow(getTableName(), null,
toContentValuesSave(entity));
entity.setId(id);
return id;
} catch (Exception e) {
throw new DatabaseException("Unable to save " + entity, e);
}
}
/**
* Calls {@link Entity#prePersist()} before converting to
* {@link ContentValues}.
*
* @param t
* @return
*/
private ContentValues toContentValuesSave(T t) {
t.prePersist();
return toContentValues(t);
}
@Override
public int update(T entity) throws DatabaseException {
try {
Long id = getId(entity);
if (id == null) {
throw new DatabaseException(
"Unable to update because Id is null in entity: "
+ entity);
}
return db.update(getTableName(), toContentValues(entity),
new StringBuffer(BaseColumns._ID).append("=").append(id)
.toString(), null);
} catch (Exception e) {
throw new DatabaseException("Unable to update " + entity, e);
}
}
@Override
public int update(Map<String, Object> values, String whereClause,
String[] whereArgs) throws DatabaseException {
try {
return db.update(getTableName(),
SqliteUtil.toContentValues(values), whereClause, whereArgs);
} catch (Exception e) {
throw new DatabaseException("Unable execute update for columns: "
+ values + "; Where: " + whereClause + ", Args: "
+ whereArgs, e);
}
}
protected Context getContext() {
return context;
}
/**
* Create a new cursor, closing the old one first.
*
* @param cursor
* @return
*/
private Cursor newCursor(Cursor cursor) {
// Make sure previous resources are released
closeCursor();
this.cursor = cursor;
return cursor;
}
/**
* Delegates to
* {@link SQLiteDatabase#query(boolean, String, String[], String, String[], String, String, String, String)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor query(boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit) {
return newCursor(db.query(distinct, table, columns, selection,
selectionArgs, groupBy, having, orderBy, limit));
}
/**
* Delegates to
* {@link SQLiteDatabase#query(String, String[], String, String[], String, String, String)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy) {
return newCursor(db.query(table, columns, selection, selectionArgs,
groupBy, having, orderBy));
}
/**
* Delegates to
* {@link SQLiteDatabase#query(String, String[], String, String[], String, String, String, String)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy, String limit) {
return newCursor(db.query(table, columns, selection, selectionArgs,
groupBy, having, orderBy, limit));
}
/**
* Delegates to
* {@link SQLiteDatabase#query(boolean, String, String[], String, String[], String, String, String, String, CancellationSignal)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor query(boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit,
CancellationSignal cancellationSignal) {
return newCursor(db.query(distinct, table, columns, selection,
selectionArgs, groupBy, having, orderBy, limit,
cancellationSignal));
}
/**
* Delegates to
* {@link SQLiteDatabase#queryWithFactory(CursorFactory, boolean, String, String[], String, String[], String, String, String, String, CancellationSignal)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*
*/
protected Cursor queryWithFactory(CursorFactory cursorFactory,
boolean distinct, String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy, String limit, CancellationSignal cancellationSignal) {
return newCursor(db.queryWithFactory(cursorFactory, distinct, table,
columns, selection, selectionArgs, groupBy, having, orderBy,
limit, cancellationSignal));
}
/**
* Delegates to
* {@link SQLiteDatabase#queryWithFactory(CursorFactory, boolean, String, String[], String, String[], String, String, String, String)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor queryWithFactory(CursorFactory cursorFactory,
boolean distinct, String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy, String limit) {
return newCursor(db.queryWithFactory(cursorFactory, distinct, table,
columns, selection, selectionArgs, groupBy, having, orderBy,
limit));
}
/**
* See
* {@link SQLiteDatabase#rawQueryWithFactory(CursorFactory, String, String[], String)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor rawQuery(String sql, String[] selectionArgs) {
return newCursor(db.rawQuery(sql, selectionArgs));
}
/**
* Delegates to
* {@link SQLiteDatabase#rawQuery(String, String[], CancellationSignal)},
* storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor rawQuery(String sql, String[] selectionArgs,
CancellationSignal cancellationSignal) {
return newCursor(db.rawQuery(sql, selectionArgs, cancellationSignal));
}
/**
* Delegates to
* {@link SQLiteDatabase#rawQueryWithFactory(CursorFactory, String, String[], String)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor rawQueryWithFactory(CursorFactory cursorFactory,
String sql, String[] selectionArgs, String editTable) {
return newCursor(db.rawQueryWithFactory(cursorFactory, sql,
selectionArgs, editTable));
}
/**
* Delegates to
* {@link SQLiteDatabase#rawQueryWithFactory(CursorFactory, String, String[], String, CancellationSignal)}
* , storing a reference of the cursor so it can be closed at any time via
* {@link #closeCursor()}.
*/
protected Cursor rawQueryWithFactory(CursorFactory cursorFactory,
String sql, String[] selectionArgs, String editTable,
CancellationSignal cancellationSignal) {
return newCursor(db.rawQueryWithFactory(cursorFactory, sql,
selectionArgs, editTable, cancellationSignal));
}
}