0

(编辑:我已经找到了我的问题的答案。这个问题仍然存在,因为出于某种原因,我必须等待 2 天才能回答我自己的问题。)

我正在开发一个将数据本地保存到 SQLite 数据库的 Android 应用程序。我想通过添加列来更新代码中的表模式,而不必删除已经保存在那里的数据,所以我使用 ALTER TABLE ADD COLUMN。

这些是我通过 db.ExecSQL 执行的确切 SQL 字符串(通过 LogCat 打印):

ALTER TABLE quests ADD COLUMN recurrence TEXT;
ALTER TABLE quests ADD COLUMN rec_comp_date TEXT;

只有当它们不存在时,我才会添加这些列,即

// If there's no recurrence column: Add it
if (cursor.getColumnIndex(QuestFeedEntry.COLUMN_NAME_RECURRENCE) == -1) {
    System.out.println("Adding recurrence column");
    query = ("ALTER TABLE " + QuestFeedEntry.TABLE_NAME
            + " ADD COLUMN " + QuestFeedEntry.COLUMN_NAME_RECURRENCE
            + TEXT_TYPE + ";");

    System.out.println(query);
    db.execSQL(query);
}

// If there's no rec_comp_date column: Add it
if (cursor.getColumnIndex(QuestFeedEntry.COLUMN_NAME_REC_COMP_DATE) == -1) {
    System.out.println("Adding recurrence date column");
    query = ("ALTER TABLE " + QuestFeedEntry.TABLE_NAME
            + " ADD COLUMN " + QuestFeedEntry.COLUMN_NAME_REC_COMP_DATE
            + TEXT_TYPE + ";");
    System.out.println(query);
    db.execSQL(query);
}

然而,在应用程序启动时执行这些查询后,当我尝试将内容保存到这些新列中时,我收到以下错误消息:

11-09 18:24:04.228: E/SQLiteLog(1763): (1) table quests has no column named recurrence
11-09 18:24:04.288: E/SQLiteDatabase(1763): Error inserting recurrence=NONE stat_type=STRENGTH description= name=a difficulty=EASY rec_comp_date=null deadline=null completed=0
11-09 18:24:04.288: E/SQLiteDatabase(1763): android.database.sqlite.SQLiteException: table quests has no column named recurrence (code 1): , while compiling: INSERT INTO 
quests(recurrence,stat_type,description,name,difficulty,rec_comp_date,deadline,completed) VALUES (?,?,?,?,?,?,?,?)

这是我正在使用的数据库类:

//Helper class for quest-related database queries.
public class QuestDatabase extends SQLiteOpenHelper {

    /* Inner class that defines the table contents */
    public static abstract class QuestFeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "quests";
        public static final String COLUMN_NAME_QUEST_NAME = "name";
        public static final String COLUMN_NAME_QUEST_DESC = "description";
        public static final String COLUMN_NAME_QUEST_TYPE = "stat_type";
        public static final String COLUMN_NAME_QUEST_DIFFICULTY = "difficulty";
        public static final String COLUMN_NAME_IS_COMPLETED = "completed";
        public static final String COLUMN_NAME_DEADLINE = "deadline";
        public static final String COLUMN_NAME_RECURRENCE = "recurrence";
        public static final String COLUMN_NAME_REC_COMP_DATE = "rec_comp_date";
    }

    private final String TEXT_TYPE = " TEXT";
    private final String COMMA_SEP = ",";
    private final String SQL_CREATE_ENTRIES = "CREATE TABLE IF NOT EXISTS "
            + QuestFeedEntry.TABLE_NAME + " (" + QuestFeedEntry._ID
            + " INTEGER PRIMARY KEY," + QuestFeedEntry.COLUMN_NAME_QUEST_NAME
            + TEXT_TYPE + COMMA_SEP + QuestFeedEntry.COLUMN_NAME_QUEST_DESC
            + TEXT_TYPE + COMMA_SEP + QuestFeedEntry.COLUMN_NAME_QUEST_TYPE
            + TEXT_TYPE + COMMA_SEP
            + QuestFeedEntry.COLUMN_NAME_QUEST_DIFFICULTY + TEXT_TYPE
            + COMMA_SEP + QuestFeedEntry.COLUMN_NAME_IS_COMPLETED + TEXT_TYPE
            + COMMA_SEP + QuestFeedEntry.COLUMN_NAME_DEADLINE + TEXT_TYPE
            + " )";

    private final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS "
            + QuestFeedEntry.TABLE_NAME;

    // If you change the database schema, you must increment the database
    // version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

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

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }

    // Call this from code whenever you changed the database structure in code
    // and need it recreated.
    public void dropTable(SQLiteDatabase db) {
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }

    // This method will add any columns to the database that don't exist.
    // These won't wipe out the current data stored, so don't need to worry
    // about backing up data.
    public void updateColumns(SQLiteDatabase db) {

        Cursor cursor = db.rawQuery("SELECT * FROM "
                + QuestFeedEntry.TABLE_NAME + " LIMIT 0", null);

        String query = "";

        // If there's no deadline column: add it
        if (cursor.getColumnIndex(QuestFeedEntry.COLUMN_NAME_DEADLINE) == -1) {
            db.execSQL("ALTER TABLE " + QuestFeedEntry.TABLE_NAME
                    + " ADD COLUMN " + QuestFeedEntry.COLUMN_NAME_DEADLINE
                    + TEXT_TYPE + ";");
        }

        // If there's no recurrence column: Add it
        if (cursor.getColumnIndex(QuestFeedEntry.COLUMN_NAME_RECURRENCE) == -1) {
            System.out.println("Adding recurrence column");
            query = ("ALTER TABLE " + QuestFeedEntry.TABLE_NAME
                    + " ADD COLUMN " + QuestFeedEntry.COLUMN_NAME_RECURRENCE
                    + TEXT_TYPE + ";");

            System.out.println(query);
            db.execSQL(query);
        }

        // If there's no rec_comp_date column: Add it
        if (cursor.getColumnIndex(QuestFeedEntry.COLUMN_NAME_REC_COMP_DATE) == -1) {
            System.out.println("Adding recurrence date column");
            query = ("ALTER TABLE " + QuestFeedEntry.TABLE_NAME
                    + " ADD COLUMN " + QuestFeedEntry.COLUMN_NAME_REC_COMP_DATE
                    + TEXT_TYPE + ";");
            System.out.println(query);
            db.execSQL(query);
        }

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy
        // is
        // to simply to discard the data and start over
        // db.execSQL(SQL_DELETE_ENTRIES);
        // onCreate(db);
    }

    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // onUpgrade(db, oldVersion, newVersion);
    }
}

以下是我用来调用此类的两种方法:

方法 1 从 quests 表中加载数据,并在主活动的 onCreate() 中调用。

public void loadQuestsFromDatabase(Activity activity) {

    if (quests == null) {
        quests = new ArrayList<Quest>();
    }

    QuestDatabase mDbHelper = new QuestDatabase(activity);
    Log.i("Debug:", "1");
    SQLiteDatabase db = mDbHelper.getReadableDatabase();
    Log.i("Debug:", "2");

    mDbHelper.onCreate(db);

    // Make sure current database is up to date by updating the columns.
    mDbHelper.updateColumns(db);

    // Define a projection that specifies which columns from the database
    // you will actually use after this query.
    String[] projection = { QuestFeedEntry.COLUMN_NAME_QUEST_NAME,
            QuestFeedEntry.COLUMN_NAME_QUEST_DESC,
            QuestFeedEntry.COLUMN_NAME_QUEST_TYPE,
            QuestFeedEntry.COLUMN_NAME_QUEST_DIFFICULTY,
            QuestFeedEntry.COLUMN_NAME_IS_COMPLETED,
            QuestFeedEntry.COLUMN_NAME_DEADLINE,
            QuestFeedEntry.COLUMN_NAME_RECURRENCE,
            QuestFeedEntry.COLUMN_NAME_REC_COMP_DATE };

    Log.i("Debug:", "3");
    Cursor cursor = db.query(QuestFeedEntry.TABLE_NAME, // The table to
                                                        // query
            projection, // The columns to return
            null, // The columns for the WHERE clause
            null, // The values for the WHERE clause
            null, // don't group the rows
            null, // don't filter by row groups
            null // The sort order
            );
    Log.i("Debug:", "4");
    cursor.moveToFirst();
    quests.clear();
    while (!cursor.isAfterLast()) {
        String name = cursor.getString(0);
        String desc = cursor.getString(1);
        StatType type = StatType.stringToType(cursor.getString(2));
        QuestDifficulty diff = QuestDifficulty.stringToDifficulty(cursor
                .getString(3));
        int completed = cursor.getInt(4);
        String deadlineStr = cursor.getString(5);
        Recurrence rec = Recurrence.NONE;
        String recStr = cursor.getString(6);
        if (recStr != null) {
            rec = Recurrence.stringToRecurrence(cursor.getString(6));
        }

        String recDateStr = cursor.getString(7);

        DateTime deadline = null;
        if (deadlineStr != null) {
            deadline = new DateTime(deadlineStr);
        }
        DateTime recCompDate = null;
        if (recDateStr != null) {
            recCompDate = new DateTime(recDateStr);
        }

        Log.i("Debug:", "Loaded quest '" + name + "' from database:");
        Log.i("Debug:", "   Name= '" + name);
        Log.i("Debug:", "   Description= '" + desc);
        Log.i("Debug:", "   Quest Type= '" + diff.toString());
        Log.i("Debug:", "   Difficulty= '" + type.toString());
        Log.i("Debug:", "   Completed= '" + Integer.toString(completed));
        Log.i("Debug:", "   Recurrence = " + rec.toString());

        if (recCompDate != null) {
            Log.i("Debug:", "   Rec Comp Date = " + recCompDate.toString());
        } else {
            Log.i("Debug:", "   Rec Comp Date = (NONE)");
        }

        Quest quest = new QuestBuilder(name, desc, diff, type, rec)
                .isComplete(completed != 0).deadline(deadline)
                .recurCompleteDate(recCompDate).build();

        if (completed == 0) {
            quests.add(quest);
        } else {
            completedQuests.add(quest);
        }

        cursor.moveToNext();
    }
    // make sure to close the cursor and database when done.
    cursor.close();
    db.close();

    Log.i("Debug:", "5");
}

方法 2 将数据保存到 quests 表中,并在主 Activity 的 onPause() 中调用。此方法中的错误发生在倒数第二行。

public void saveQuestsToDatabase(Activity activity) {

    QuestDatabase mDbHelper = new QuestDatabase(activity);

    // Gets the data repository in write mode
    SQLiteDatabase db = mDbHelper.getWritableDatabase();

    // Drop the table first so that we can recreate it with all quests
    // loaded in the arraylist
    mDbHelper.dropTable(db);

    ArrayList<Quest> allQuests = getAllQuests();

    for (int i = 0; i < allQuests.size(); i++) {

        Quest quest = allQuests.get(i);

        // For now, don't save completed quests so it doesn't clog up the
        // database when debugging.
        if (quest.getIsComplete()) {
            continue;
        }

        // Create a new map of values, where column names are the keys
        ContentValues values = new ContentValues();
        values.put(QuestFeedEntry.COLUMN_NAME_QUEST_NAME, quest.getName());
        values.put(QuestFeedEntry.COLUMN_NAME_QUEST_DESC,
                quest.getDescription());
        values.put(QuestFeedEntry.COLUMN_NAME_QUEST_TYPE, quest
                .getStatType().toString());
        values.put(QuestFeedEntry.COLUMN_NAME_QUEST_DIFFICULTY, quest
                .getDifficulty().toString());
        values.put(QuestFeedEntry.COLUMN_NAME_IS_COMPLETED,
                (quest.getIsComplete() ? 1 : 0));

        if (quest.isTimed()) {
            values.put(QuestFeedEntry.COLUMN_NAME_DEADLINE, quest
                    .getDeadline().toString());
        } else {
            values.putNull(QuestFeedEntry.COLUMN_NAME_DEADLINE);
        }

        values.put(QuestFeedEntry.COLUMN_NAME_RECURRENCE, quest
                .getRecurrence().toString());

        if (quest.isTempComplete()) {
            values.put(QuestFeedEntry.COLUMN_NAME_REC_COMP_DATE, quest
                    .getRecCompDate().toString());
        } else {
            values.putNull(QuestFeedEntry.COLUMN_NAME_REC_COMP_DATE);
        }

        Log.i("Debug:", "Saved quest '" + quest.getName()
                + "' in database:");
        Log.i("Debug:", "   Name= '" + quest.getName());
        Log.i("Debug:", "   Description= '" + quest.getDescription());
        Log.i("Debug:", "   Quest Type= '" + quest.getStatType().toString());
        Log.i("Debug:", "   Difficulty= '" + quest.getDifficulty().toString());
        Log.i("Debug:", "   Completed= '"
                + (quest.getIsComplete() ? "true" : "false"));
        Log.i("Debug:", "   Recurrence= "
                + quest.getRecurrence().toString());

        if (quest.getRecCompDate() != null) {
            Log.i("Debug:", "   Rec Comp Date = "
                    + quest.getRecCompDate().toString());
        } else {
            Log.i("Debug:", "   Rec Comp Date = (NONE)");
        }

        // Insert the new row, returning the primary key value of the new
        // row
        long newRowId;
        //ERROR HAPPENS ON THIS LINE
        newRowId = db.insert(QuestFeedEntry.TABLE_NAME, "null", values);
    }

    // Make sure to close the database when done
    db.close();

}
4

2 回答 2

0

您没有指定在哪里执行这些调用以添加新列,但您确实应该在SQLiteOpenHelper#onUpgrade中执行此操作。这样你就知道它是在你第一次通过SQLiteOpenHelper#getWritableDatabase调用数据库对象时执行的。

于 2013-11-10T00:40:48.627 回答
0

我发现了我的错误。我在保存 saveQuestsToDatabase() 之前调用了 dropTable,这最终会重新创建旧版本的表。

于 2013-11-10T00:52:14.783 回答