2010-07-25 5 views
3

Это моя первая публикация на этот сайт, поэтому, надеюсь, это будет положительный опыт. У меня проблемы с Android SQLite/ContentProvider, которые я избивал головой против стены в течение последних 3 часов. Ниже приведен код ContentProvider:Android SQLite Insert working, Query not working

public class IncidentProvider extends ContentProvider { 

private static final UriMatcher sUriMatcher; 
private static final HashMap<String, String> projectionMap; 
private static final int INCIDENTS = 1; 

public static final String AUTHORITY = "com.test.providers.IncidentsProvider"; 
public static final String TABLE_NAME = "incidents"; 

private static class DatabaseHelper extends SQLiteOpenHelper { 

    public DatabaseHelper(Context context) { 
     super(context, context.getString(R.string.database_name), null, Integer.parseInt(context.getString(R.string.database_version))); 
    } 

    @Override 
    public void onCreate(SQLiteDatabase db) { 
     db.execSQL("CREATE TABLE " + TABLE_NAME + " (" 
       + Incidents.INCIDENT_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," 
       + Incidents.NAME + " VARCHAR(30)," 
       + Incidents.CREATE_TIME + " LONG," 
       + Incidents.LAST_UPDATE + " LONG," 
       + Incidents.IS_ACTIVE + " VARCHAR(30)" + ");"); 
    } 

    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
     db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); 
     onCreate(db); 
    } 

} 

private DatabaseHelper dbHelper; 

/** 
* Delete a row from the database 
*/ 
@Override 
public int delete(Uri uri, String where, String[] whereArgs) { 
    SQLiteDatabase db = dbHelper.getWritableDatabase(); 
    int count; 
    switch(sUriMatcher.match(uri)) { 
     case INCIDENTS: 
      count = db.delete(TABLE_NAME, where, whereArgs); 
      break; 
     default: 
      throw new IllegalArgumentException("Unknown URI " + uri); 
    } 
    getContext().getContentResolver().notifyChange(uri, null); 
    db.close(); 
    return count; 
} 

/** 
* Return the content type managed by this ContentProvider 
*/ 
@Override 
public String getType(Uri uri) { 
    switch(sUriMatcher.match(uri)) { 
     case INCIDENTS: 
      return Incidents.CONTENT_TYPE; 
     default: 
      throw new IllegalArgumentException("UNKNOWN URI " + uri); 
    } 
} 

/** 
* Insert new content into a row in the database 
*/ 
@Override 
public Uri insert(Uri uri, ContentValues initialValues) { 
    if(sUriMatcher.match(uri) != INCIDENTS) 
     throw new IllegalArgumentException("UNKNOWN URI " + uri); 

    ContentValues values; 
    if(initialValues != null) { 
     values = new ContentValues(initialValues); 
    } else { 
     values = new ContentValues(); 
    } 

    SQLiteDatabase db = dbHelper.getWritableDatabase(); 
    long rowId = db.insert(TABLE_NAME, Incidents.NAME, values); 

    if(rowId > 0) { 
     Uri incidentUri = ContentUris.withAppendedId(Incidents.CONTENT_URI, rowId); 
     getContext().getContentResolver().notifyChange(incidentUri, null); 
     db.close(); 
     return incidentUri; 
    } 
    db.close(); 
    throw new SQLException("Failed to insert row into " + uri); 
} 

/** 
* Called when creating this ContentProvider 
*/ 
@Override 
public boolean onCreate() { 
    dbHelper = new DatabaseHelper(getContext()); 
    return false; 
} 

/** 
* Called when making a query to this content provider 
*/ 
@Override 
public Cursor query(Uri uri, String[] projection, String selection, 
     String[] selectionArgs, String sortOrder) { 

    SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 

    switch(sUriMatcher.match(uri)) { 
     case INCIDENTS: 
      qb.setTables(TABLE_NAME); 
      qb.setProjectionMap(projectionMap); 
      break; 
     default: 
      throw new IllegalArgumentException("UNKNOWN URI " + uri); 
    } 

    SQLiteDatabase db = dbHelper.getWritableDatabase(); 

    Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); 
    c.setNotificationUri(getContext().getContentResolver(), uri); 
    db.close(); 
    return c; 
} 

/** 
* Called when updating a row through this content provider 
*/ 
@Override 
public int update(Uri uri, ContentValues values, String where, 
     String[] whereArgs) { 
    SQLiteDatabase db = dbHelper.getWritableDatabase(); 
    int count; 

    switch(sUriMatcher.match(uri)) { 
     case INCIDENTS: 
      count = db.update(TABLE_NAME, values, where, whereArgs); 
      break; 
     default: 
      throw new IllegalArgumentException("UNKNOWN URI " + uri); 
    } 

    getContext().getContentResolver().notifyChange(uri, null); 
    db.close(); 
    return count; 
} 

static { 
    sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 
    sUriMatcher.addURI(AUTHORITY, TABLE_NAME, INCIDENTS); 

    projectionMap = new HashMap<String, String>(); 
    projectionMap.put(Incidents.INCIDENT_ID, Incidents.INCIDENT_ID); 
    projectionMap.put(Incidents.NAME, Incidents.NAME); 
    projectionMap.put(Incidents.CREATE_TIME, Incidents.CREATE_TIME); 
    projectionMap.put(Incidents.LAST_UPDATE, Incidents.LAST_UPDATE); 
    projectionMap.put(Incidents.IS_ACTIVE, Incidents.IS_ACTIVE); 
} 

}

Я также написал дб вспомогательный класс, чтобы сделать его легко взаимодействовать с ContentProvider. 2 Например, методы DBHelper следующие (1 для вставки, 1 для запроса).

/** 
    * Adds a new incident to the database 
    **/ 
public void addNewIncident(ContentResolver contentResolver, Incident incident) { 
    ContentValues contentValues = new ContentValues(); 
    contentValues.put(Incidents.NAME, incident.getName()); 
    contentValues.put(Incidents.CREATE_TIME, incident.getCreateTime()); 
    contentValues.put(Incidents.LAST_UPDATE, incident.getLastUpdate()); 
    contentValues.put(Incidents.IS_ACTIVE, incident.isActive()?"true":"false"); 

    contentResolver.insert(Incidents.CONTENT_URI, contentValues); 

} 

    /** 
    * Retrieves all incidents from the database 
    **/ 
    public ArrayList<Incident> getIncidents(ContentResolver contentResolver) { 
    Cursor c = contentResolver.query(Incidents.CONTENT_URI, null, null, null, null); 
    ArrayList<Incident> returnList = new ArrayList<Incident>(); 

    if(c!=null && c.moveToNext()) { 
    for(int i=0; i<c.getCount(); i++) { 
    c.moveToPosition(i); 
    Incident incident = new Incident(c.getString(c.getColumnIndex(Incidents.NAME))); 
    incident.setCreateTime(c.getLong(c.getColumnIndex(Incidents.CREATE_TIME))); 
    incident.setLastUpdate(c.getLong(c.getColumnIndex(Incidents.LAST_UPDATE))); 
    incident.setActive(c.getString(c.getColumnIndex(Incidents.IS_ACTIVE)).equalsIgnoreCase("true")); 
    returnList.add(incident); 
    } 
    c.close(); 
    } 

    return returnList; 
} 

Хорошо, хорошо! Итак, вот моя проблема. Я могу без проблем вставлять в базу данных! Если я запрошу базу данных через оболочку adb, я смогу увидеть все вставленные записи. Когда я запрашиваю SQLiteQueryBuilder или SQLiteDatabase.rawQuery или любой другой метод запроса, возвращаемый курсор возвращается с -1. Не имеет значения, запрашиваю ли я отдельную запись или запрашиваю всю базу данных. Запрос все равно возвращается -1.

Любые идеи? Я пропустил что-то невероятно простое?

Заранее благодарим любого, кто хочет помочь разочарованному человеку!

UPDATE:

Ниже приведен пример кода для вставки (который работает), и запрашивая (который не работает)

/** 
* Following db insert works 
*/ 
IncidentsDB db = IncidentsDB.getInstance(); 
workingIncident.setLastUpdate(System.currentTimeMillis());  // isActive, and createTime set in constructor 
db.addNewIncident(this.getContentResolver(), workingIncident); 

/** 
* Following db queries do not work, cursor ends up with mCount=-1 
*/ 
Cursor c = IncidentsDB.getInstance().getIncidents(this.getContentResolver()); 
+0

Отлично, спасибо за редактирования. Прежде всего, я бы рекомендовал вам загрузить SQLite Database Browser для проверки ваших запросов. Это поможет вам гарантировать, что запросы, которые вы помещаете в rawQuery, работают в базе данных (вы можете экспортировать базы данных своего приложения с точки зрения DDMS в Eclipse: File explorer Data/data/name.of.you.package/databases/nameOfTheDatabase.db – Sephy

+0

Можете ли вы опубликовать пример критериев, используемых как для строителя, так и для необработанных запросов? –

+0

Вы пробовали contentResolver.rawQuery («выберите * из» + Incidents.CONTENT_URI, null) ;? – Sephy

ответ

3

Ну, похоже, что я установил его. Оказывается, лучше закрыть базу данных в методе finalize, а не открывать и закрывать в каждом из методов удаления, вставки, запроса и обновления, как у меня в опубликованном выше коде. Вот фрагмент кода для тех из вас, которые столкнулись с этой проблемой, как я сделал:

// add SQLiteDatabase member variable to content provider 
private SQLiteDatabase db; 

/** 
* Delete a row from the database 
*/ 
@Override 
public int delete(Uri uri, String where, String[] whereArgs) { 
    // use Content Provider's db member variable in the following code block 
    int count; 
    switch(sUriMatcher.match(uri)) { 
     case INCIDENTS: 
      count = db.delete(TABLE_NAME, where, whereArgs); 
      break; 
     default: 
      throw new IllegalArgumentException("Unknown URI " + uri); 
    } 
    getContext().getContentResolver().notifyChange(uri, null); 
    return count; 
} 

Затем добавьте следующий ФИНАЛИЗ код поставщика контента

@Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 
     db.close(); 
    } 
+0

Рад, что вы решили свой вопрос, извините, я не видел ответа на ваш комментарий раньше! –

+0

Не проблема! Большое спасибо за помощь. Я очень ценю быстрый ответ. – Nick

+0

Упрощение: никогда не называть 'db.close()'; «ContentProvider» будет создан один раз и исчезнет, ​​когда ваш процесс будет убит, что приведет к закрытию соединений с базой данных. –