(编辑:我已经找到了我的问题的答案。这个问题仍然存在,因为出于某种原因,我必须等待 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();
}