0

我正在制作一个在 SQLite 数据库中搜索餐馆的 android 应用程序。我目前使用自定义加载器(我在网上找到)从数据库加载游标,然后获取该游标并使用 SimpleCursorAdapter 填充列表视图。我希望绑定所有数据,但只显示餐厅的名称(因为当我单击列表项时,我想获取所有数据并将其发送到显示数据的新活动)。如果我指定餐厅名称和餐厅类型作为搜索参数,我会运行原始查询:

 "SELECT * FROM Restaurant WHERE name = " + name + " AND rType = " + restaurantType; 

在我的 SQLiteCursorLoader 类中,我收到一条错误消息,指出列 rType 不存在,即使我知道该列存在于 Restuarant 表中。当我的查询中有两个 WHERE 子句时,我似乎得到了这个错误,但是当我有一个 WHERE 子句时,这不会发生。任何帮助将不胜感激!这是我的代码:

餐厅搜索结果活动:

package com.studentapplication;
import android.app.Activity;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

public class RestaurantSearchResultsActivity extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor>{

DatabaseHelper databaseHelper;
SimpleCursorAdapter mAdapter;
Bundle searchParameters;

@Override
public void onCreate(Bundle savedInstanceState)
{
    Log.w("CoolBeans", "Results Activity Created");
    super.onCreate(savedInstanceState);

    databaseHelper = new DatabaseHelper(getApplicationContext());
    //setContentView(R.layout.restaurant_search_results);

    Bundle searchParameters = getIntent().getExtras();
    String n = searchParameters.getString("name");
    Log.i("0)", n);


    //first N columns should match toViews first N views
    String[] fromColumns = {getString(R.string.database_column_name),
                           getString(R.string.database_column_restaurant_type),
                           getString(R.string.database_column_phone_number),
                           getString(R.string.database_column_address),
               getString(R.string.database_column_csz),
               getString(R.string.database_column_brief_description),
               getString(R.string.database_column_url),
               getString(R.string.database_column_price)};

    int[] toViews = {android.R.id.text1};

    mAdapter = new SimpleCursorAdapter( getApplicationContext(),
                        this.getListView().getId(),
                        null,
                            fromColumns,
                        toViews,
                        0);
    setListAdapter(mAdapter);
    getLoaderManager().initLoader(0, searchParameters, this);       
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle searchParameters) {
    Log.i("3)", "Correct Loader Being Built");
    return new SQLiteCursorLoader(getApplicationContext(),
            databaseHelper,
            searchParameters,
            null);

}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    mAdapter.swapCursor(data);

}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mAdapter.swapCursor(null);

}

@Override
    public void onListItemClick(ListView l, View v, int position, long id)
{
    Cursor myCursor = (Cursor)l.getItemAtPosition(position);
    if(l.isItemChecked(position) == true)
    {
        l.setItemChecked(position, false);
        //shut down previously selected item details

    }
    else
    {
        l.setItemChecked(position, true);

        //start previously selected item details

    }
}


}

SQLiteCursorLoader:

package com.studentapplication;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.os.Bundle;
import android.util.Log;

public class SQLiteCursorLoader extends AbstractCursorLoader
{
SQLiteOpenHelper db=null;
String rawQuery=null;
String[] args=null;

/**
 * Creates a fully-specified SQLiteCursorLoader. See
 * {@link SQLiteDatabase#rawQuery(SQLiteDatabase, String, String[])
 * SQLiteDatabase.rawQuery()} for documentation on the
 * meaning of the parameters. These will be passed as-is
 * to that call.
 */
public SQLiteCursorLoader(Context context, SQLiteOpenHelper db, Bundle
    searchParameters, String[] args)
{
    super(context);
    this.db=db;
    this.rawQuery=createQuery(searchParameters);
    this.args=args;
}

private String createQuery(Bundle searchParameters) {
    String name = "\'";
    name = name + searchParameters.getString("name");
    name = name + "\'";
    int price = searchParameters.getInt("price");
    String restaurantType = "\'";
    restaurantType = restaurantType +
            searchParameters.getString("restaurant_type");
    restaurantType = restaurantType + "\'";

    //String dist = searchParameters.getString("dist");

    String theQuery = "";

    if(!name.equals("\'\'"))
    {
        if(!restaurantType.equals("\'_\'"))
        {
            theQuery = "SELECT * FROM Restaurant WHERE name 
= "+ name + " AND rType = " + restaurantType;
        }
        else if(price != 0)
        {
            theQuery = "SELECT * FROM Restaurant WHERE name = " + name
+ " AND price = " + ((Integer)price).toString();
        }
        else
        {
            theQuery="SELECT * FROM Restaurant WHERE name = " + name;
        }
    }
    else if(!restaurantType.equals("\'\'"))
    {
        theQuery = "SELECT * FROM Restaurant WHERE type = " +
restaurantType + " SORT BY name"; 
    }
    else if(price != 0)
    {
        theQuery="SELECT * FROM Restaurant WHERE price=" + price + " SORT
BY name";
    }



    return theQuery;
}


/**
 * Runs on a worker thread and performs the actual
 * database query to retrieve the Cursor.
 */
@Override
protected Cursor buildCursor()
{
    return(db.getReadableDatabase().rawQuery(rawQuery, args));
}

/**
 * Writes a semi-user-readable roster of contents to
 * supplied output.
 */
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[]
    args)
{
    super.dump(prefix, fd, writer, args);
    writer.print(prefix);
    writer.print("rawQuery=");
    writer.println(rawQuery);
    writer.print(prefix);
    writer.print("args=");
    writer.println(Arrays.toString(args));
}

public void insert(String table, String nullColumnHack, ContentValues values)
{
    new InsertTask(this).execute(db, table, nullColumnHack, values);
}

public void update(String table, ContentValues values, String whereClause, String[]
    whereArgs)
{
    new UpdateTask(this).execute(db, table, values, whereClause, whereArgs);
}

public void replace(String table, String nullColumnHack, ContentValues values)
{
    new ReplaceTask(this).execute(db, table, nullColumnHack, values);
}

public void delete(String table, String whereClause, String[] whereArgs)
{
    new DeleteTask(this).execute(db, table, whereClause, whereArgs);
}

public void execSQL(String sql, Object[] bindArgs)
{
    new ExecSQLTask(this).execute(db, sql, bindArgs);
}

//Private Classes

private class InsertTask extends ContentChangingTask<Object, Void, Void>
{
    InsertTask(SQLiteCursorLoader loader)
    {
        super(loader);
    }

    @Override
    protected Void doInBackground(Object... params)
    {
        SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
        String table=(String)params[1];
        String nullColumnHack=(String)params[2];
        ContentValues values=(ContentValues)params[3];

        db.getWritableDatabase().insert(table, nullColumnHack, values);

        return(null);
    }
}

private class UpdateTask extends ContentChangingTask<Object, Void, Void>
{
    UpdateTask(SQLiteCursorLoader loader)
    {
        super(loader);
    }

    @Override
    protected Void doInBackground(Object... params)
    {
        SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
        String table=(String)params[1];
        ContentValues values=(ContentValues)params[2];
        String where=(String)params[3];
        String[] whereParams=(String[])params[4];

        db.getWritableDatabase().update(table, values, where, whereParams);

        return(null);
    }
}

private class ReplaceTask extends ContentChangingTask<Object, Void, Void>
{
    ReplaceTask(SQLiteCursorLoader loader)
    {
        super(loader);
    }

    @Override
    protected Void doInBackground(Object... params)
    {
        SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
        String table=(String)params[1];
        String nullColumnHack=(String)params[2];
        ContentValues values=(ContentValues)params[3];

        db.getWritableDatabase().replace(table, nullColumnHack, values);

        return(null);
    }
}

private class DeleteTask extends ContentChangingTask<Object, Void, Void>
{
    DeleteTask(SQLiteCursorLoader loader)
    {
        super(loader);
    }

    @Override
    protected Void doInBackground(Object... params)
    {
        SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
        String table=(String)params[1];
        String where=(String)params[2];
        String[] whereParams=(String[])params[3];

        db.getWritableDatabase().delete(table, where, whereParams);

        return(null);
    }
}

private class ExecSQLTask extends ContentChangingTask<Object, Void, Void>
{
    ExecSQLTask(SQLiteCursorLoader loader)
    {
        super(loader);
    }

    @Override
    protected Void doInBackground(Object... params)
    {
        SQLiteOpenHelper db=(SQLiteOpenHelper)params[0];
        String sql=(String)params[1];
        Object[] bindParams=(Object[])params[2];

        db.getWritableDatabase().execSQL(sql, bindParams);

     return(null);
    }
}
}

AbstractCursorLoader:

package com.studentapplication;

import android.annotation.TargetApi;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.database.Cursor;
import android.os.Build;

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
abstract public class AbstractCursorLoader extends AsyncTaskLoader<Cursor> {

abstract protected Cursor buildCursor();
Cursor lastCursor=null;

public AbstractCursorLoader(Context context) {
  super(context);
}

/** 
 * Runs on a worker thread, loading in our data. Delegates
 * the real work to concrete subclass' buildCursor() method. 
 */
@Override
public Cursor loadInBackground() {
  Cursor cursor=buildCursor();

  if (cursor!=null) {
    // Ensure the cursor window is filled
    cursor.getCount();
  }

  return(cursor);
}

/**
 * Runs on the UI thread, routing the results from the
 * background thread to whatever is using the Cursor
 * (e.g., a CursorAdapter).
 */
@Override
public void deliverResult(Cursor cursor) {
  if (isReset()) {
    // An async query came in while the loader is stopped
    if (cursor!=null) {
      cursor.close();
    }

    return;
  }

  Cursor oldCursor=lastCursor;
  lastCursor=cursor;

  if (isStarted()) {
    super.deliverResult(cursor);
  }

  if (oldCursor!=null && oldCursor!=cursor && !oldCursor.isClosed()) {
    oldCursor.close();
  }
}

/**
 * Starts an asynchronous load of the list data.
 * When the result is ready the callbacks will be called
 * on the UI thread. If a previous load has been completed
 * and is still valid the result may be passed to the
 * callbacks immediately.
 * 
 * Must be called from the UI thread.
 */
@Override
protected void onStartLoading() {
  if (lastCursor!=null) {
    deliverResult(lastCursor);
  }

  if (takeContentChanged() || lastCursor==null) {
    forceLoad();
  }
}

/**
* Must be called from the UI thread, triggered by a
* call to stopLoading().
*/
@Override
protected void onStopLoading() {
  // Attempt to cancel the current load task if possible.
  cancelLoad();
}

/**
 * Must be called from the UI thread, triggered by a
 * call to cancel(). Here, we make sure our Cursor
 * is closed, if it still exists and is not already closed.
 */
@Override
public void onCanceled(Cursor cursor) {
  if (cursor!=null && !cursor.isClosed()) {
    cursor.close();
  }
}

/**
 * Must be called from the UI thread, triggered by a
 * call to reset(). Here, we make sure our Cursor
 * is closed, if it still exists and is not already closed.
 */
@Override
protected void onReset() {
  super.onReset();

  // Ensure the loader is stopped
  onStopLoading();

  if (lastCursor!=null && !lastCursor.isClosed()) {
    lastCursor.close();
  }

  lastCursor=null;
  }
  }

数据库助手:

package com.studentapplication;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {

// All Static variables
// Database Version
private static final int DATABASE_VERSION = 1;

// Database Name
private static final String DATABASE_NAME = "LOCAL_DATABASE";

// table Names
private static final String TABLE_Restaurant = "Restaurant";
private static final String TABLE_Coupon = "Coupon";
private static final String TABLE_Linker = "Linker";

//restaurant columns
private static final String R_Name = "name";
private static final String R_Description = "description";
private static final String R_Address = "address";
private static final String R_CSZ = "CSZ";
private static final String R_URL = "url";
private static final String R_Price = "price";
private static final String R_Type = "rType";
private static final String R_ID = "_id";

//coupon columns
private static final String C_ID = "_id";
private static final String C_NAME = "name";
private static final String C_Description = "description";

//linker table columns
private static final String r_id = "r_id";
private static final String c_id = "c_id";
private static final String date = "date";
private static final String l_id = "l_id";

public DatabaseHelper(Context context)
{
    super(context, DATABASE_NAME, null, DATABASE_VERSION);

}

public DatabaseHelper(Context context, String name, CursorFactory factory,
        int version) {
    super(context, name, factory, version);
}

// Creating Tables
@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_Restaurant_TABLE = "CREATE TABLE " + TABLE_Restaurant + "( "
            + R_ID + " INTEGER PRIMARY KEY, "
            + R_Name + " TEXT NOT NULL, " 
            + R_Type + " TEXT NOT NULL, "
            + R_Address + " TEXT NOT NULL, " 
            + R_CSZ + " TEXT NOT NULL, "
            + R_Description + " TEXT NOT NULL, " 
            + R_URL + " TEXT NOT NULL, "
            + R_Price +" INTEGER NOT NULL"
            + " )";

    String CREATE_Coupon_TABLE = "CREATE TABLE " + TABLE_Coupon + "("
            + C_ID + " INTEGER PRIMARY KEY, " 
            + C_NAME + " TEXT, "
            + C_Description + " TEXT"
            + ")";

    String CREATE_Linker_TABLE = "CREATE TABLE " + TABLE_Linker + "("
            + r_id + " INTEGER, " 
            + c_id + " INTEGER, " 
            + date + " DATE, "
            + l_id + "INTEGER PRIMARY KEY, "
            + "FOREIGN KEY(r_id) REFERENCES Restaurant(_id), "
            + "FOREIGN KEY(c_id) REFERENCES Coupon(_id) )";

    db.execSQL(CREATE_Restaurant_TABLE);
    db.execSQL(CREATE_Coupon_TABLE);
    db.execSQL(CREATE_Linker_TABLE);
}

// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // Drop older table if existed
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_Restaurant);
    // Create tables again
    onCreate(db);
}

}

堆栈跟踪:

04-23 13:47:29.691: E/SQLiteLog(2600): (1) no such column: rType
04-23 13:47:29.691: W/dalvikvm(2600): threadid=11: thread exiting with uncaught
    exception (group=0x40a13300)
04-23 13:47:29.852: E/AndroidRuntime(2600): FATAL EXCEPTION: AsyncTask #1
04-23 13:47:29.852: E/AndroidRuntime(2600): java.lang.RuntimeException: An error
    occured while executing doInBackground()
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.os.AsyncTask$3.done(AsyncTask.java:299)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.util.concurrent.FutureTask.setException(FutureTask.java:124)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.util.concurrent.FutureTask.run(FutureTask.java:137)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.lang.Thread.run(Thread.java:856)
04-23 13:47:29.852: E/AndroidRuntime(2600): Caused by:
    android.database.sqlite.SQLiteException: no such column: rType (code 1): , while
    compiling: SELECT * FROM Restaurant WHERE name = 'RName' AND rType = 'Fancy'
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.database.sqlite.SQLiteConnection.acquirePreparedStatement
    (SQLiteConnection.java:882)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:493)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.database.sqlite.SQLiteDirectCursorDriver.query
    (SQLiteDirectCursorDriver.java:44)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
   android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1314)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
   android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1253)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    com.studentapplication.SQLiteCursorLoader.buildCursor(SQLiteCursorLoader.java:88)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    com.studentapplication.AbstractCursorLoader.loadInBackground
    (AbstractCursorLoader.java:24)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    com.studentapplication.AbstractCursorLoader.loadInBackground
    (AbstractCursorLoader.java:1)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:301)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:68)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:56)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    android.os.AsyncTask$2.call(AsyncTask.java:287)
04-23 13:47:29.852: E/AndroidRuntime(2600):     at
    java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
04-23 13:47:29.852: E/AndroidRuntime(2600):     ... 4 more
04-23 13:47:29.902: W/ActivityManager(161):   Force finishing activity
    com.studentapplication/.RestaurantSearchResultsActivity
04-23 13:47:29.902: W/WindowManager(161): Failure taking screenshot for (266x425) to
    layer 21020
4

1 回答 1

0

在生产中,当您修改数据库的架构时,您会碰到传递给SQLiteOpenHelper构造函数的架构版本号,因此SQLiteOpenHelper会调用onUpgrade()并影响更改。

在开发中,如果您愿意,您也可以这样做。但是,很容易忘记这一点。因此,如果您的数据库结构似乎与预期不符,则:

  • 碰撞该架构版本号,以强制onUpgrade()调用,或

  • 通过卸载、“清除数据”从您的应用程序的“设置”条目等中清除现有数据库,以强制调用onCreate()您的SQLiteOpenHelper

于 2013-04-23T18:25:21.900 回答