android - 更新表后的 Ormlite 我不能对所有方法使用查询

标签 android database sqlite ormlite sqlcipher

我正在开发一个使用 ormlitesqlcipher 的应用程序。我需要在用户丢失所有数据的情况下重命名列。但是在更新该列之后 - 我无法使用 ormlite 的 queryForAll 方法。我试着寻找问题,但一点运气也没有。提前谢谢你。

这是我的更新表方法:

    List<DB_RecordItem> recordItems;
    List<JSONObject> objects = new ArrayList<>();
    try {
        // Add new column - file_id (instead of 'file')
        db.execSQL(String.format("ALTER TABLE %s ADD COLUMN %s INTEGER;", DB_RecordItem.DB_TABLE, DB_File.DB_FILE));

        // Get all items, which are going to be stored later.
        recordItems = Database.getRecordItems().queryForAll();

        Dao<DB_RecordItem, String> dao = DaoManager.createDao(connectionSource, DB_RecordItem.class);
        Log.i("DatabaseLog", "Before delete table: " + dao.queryRaw("PRAGMA table_info(db_recorditem)").getResults().toString());

        QueryBuilder<DB_RecordItem, String> qb = dao.queryBuilder();

        // Query for all items.
        for (DB_RecordItem item : recordItems) {
            JSONObject jsonObject = new JSONObject(item.toJson());

            Log.i("DatabaseLog", "Before: " + jsonObject.toString());

            qb.where().eq("id", item.getId());
            CloseableIterator<DB_RecordItem> iterator = dao.iterator(qb.prepare());
            // Get old file id, since model id is changed to "file_id"
            GenericRawResults<String[]> rawResults = dao.queryRaw("select file from db_recorditem where id = " + item.getId());

            try {
                int file_id = Integer.valueOf(rawResults.getResults().get(0)[0]);
                    if (file_id != -1) {
                        // If file_id it is not -1, then the recrod item had a file_id.
                        jsonObject.put(DB_File.DB_FILE, file_id);
                    }
            } finally {
                iterator.closeQuietly();
            }

            // Set all ids to 0, autoincrement will do its thing.
            jsonObject.remove(DB_Core.DB_ID);
            jsonObject.put(DB_Core.DB_ID, 0);
            // Store new data.
            objects.add(jsonObject);
        }

        // Drop old table, with column that we don't need any more.
        TableUtils.dropTable(connectionSource, DB_RecordItem.class, false);
        // Create new table with correct column naming.
        TableUtils.createTable(connectionSource, DB_RecordItem.class);

        Log.i("DatabaseLog", "New table: " + dao.queryRaw("PRAGMA table_info(db_recorditem)").getResults().toString());

        // Store all data.
        for (JSONObject object : objects) {
            Log.i("DatabaseLog", "After: " + object.toString());
            new DB_RecordItem(object).save();
        }

        Log.i("DatabaseLog", "Done!");
    } catch (Exception e) {
        Log.i("DatabaseLog", "Crash.");
        e.printStackTrace();
    }

上面的代码似乎可以完美运行,没有任何错误。但是,在我尝试继续执行此行后出现错误:

// getRecordItems() returns Dao<DB_RecordItem, Integer>
Database.getRecordItems().queryForAll()

看来问题出在新插入的字段上。如果字段没有“file_id”的值,一切正常。但如果确实如此 - 错误会重现。我是否有可能需要重新生成 ormlite config_file

我的错误信息是:

E/CursorWindow﹕ Bad request for field slot 0,17. numRows = 1, numColumns = 17
W/System.err﹕ java.lang.IllegalStateException: get field slot from row 0 col 17 failed
W/System.err﹕ at net.sqlcipher.CursorWindow.getLong_native(Native Method) 
W/System.err﹕ at net.sqlcipher.CursorWindow.getInt(CursorWindow.java:522)
W/System.err﹕ at net.sqlcipher.AbstractWindowedCursor.getInt(AbstractWindowedCursor.java:95)
W/System.err﹕ at android.database.CursorWrapper.getInt(CursorWrapper.java:102)
W/System.err﹕ at com.j256.ormlite.sqlcipher.android.AndroidDatabaseResults.getInt(AndroidDatabaseResults.java:168)
W/System.err﹕ at com.j256.ormlite.field.types.IntegerObjectType.resultToSqlArg(IntegerObjectType.java:37)
W/System.err﹕ at com.j256.ormlite.field.BaseFieldConverter.resultToJava(BaseFieldConverter.java:24)
W/System.err﹕ at com.j256.ormlite.field.FieldType.resultToJava(FieldType.java:819)
W/System.err﹕ at com.j256.ormlite.stmt.mapped.BaseMappedQuery.mapRow(BaseMappedQuery.java:60)
W/System.err﹕ at com.j256.ormlite.stmt.SelectIterator.getCurrent(SelectIterator.java:270)
W/System.err﹕ at com.j256.ormlite.stmt.SelectIterator.nextThrow(SelectIterator.java:161)
W/System.err﹕ at com.j256.ormlite.stmt.StatementExecutor.query(StatementExecutor.java:202)
W/System.err﹕ at com.j256.ormlite.stmt.StatementExecutor.queryForAll(StatementExecutor.java:118)
W/System.err﹕ at com.j256.ormlite.dao.BaseDaoImpl.queryForAll(BaseDaoImpl.java:241)
W/System.err﹕ at database.Database$ObjectsIterator.start(Database.java:221)
W/System.err﹕ at fragments.F_BackupLocal$LocalBackup.doInBackground(F_BackupLocal.java:429)
W/System.err﹕ at fragments.F_BackupLocal$LocalBackup.doInBackground(F_BackupLocal.java:368)
W/System.err﹕ at android.os.AsyncTask$2.call(AsyncTask.java:288)
W/System.err﹕ at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err﹕ at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)

只要找到问题所在,我就会更新这篇文章。

最佳答案

我终于解决了这个问题。

According to this documentation - In SQLite, all you can do is rename a table name and add a new column. You can’t rename or remove a column or change the constraints.

我最终的解决方案用新字段追加表格,并保留旧字段。

private void renameDB_Record_Item_Columns(ConnectionSource connectionSource) {
    List<DB_RecordItem> recordItems;
    List<JSONObject> objects = new ArrayList<>();
    try {
        Dao<DB_RecordItem, String> dao = DaoManager.createDao(connectionSource, DB_RecordItem.class);
        dao.executeRaw(String.format("ALTER TABLE %s ADD COLUMN %s INTEGER;", "db_recorditem", "file_id"));

        recordItems = Database.getRecordItems().queryForAll();
        QueryBuilder<DB_RecordItem, String> qb = dao.queryBuilder();
        // Query for all items.
        for (DB_RecordItem item : recordItems) {
            qb.where().eq("id", item.getId());
            CloseableIterator<DB_RecordItem> iterator = dao.iterator(qb.prepare());

            GenericRawResults<String[]> rawResults = dao.queryRaw("select file from db_recorditem where id = " + item.getId() + ";");

            try {
                int file_id = Integer.valueOf(rawResults.getResults().get(0)[0]);
                    if (file_id != -1) {
                        // If file_id it is not -1, then the recrod item had a file_id.
                        item.assignFile(new DB_File().setId(file_id));
                        item.update();
                    }
            } finally {
                iterator.closeQuietly();
            }
        }
    } catch (Exception e) {
        Log.i("DatabaseLog", "Crash.");
        e.printStackTrace();
    }
}

无论如何,如果有人找到删除和重新创建表的正确方法,请分享。 :)

关于android - 更新表后的 Ormlite 我不能对所有方法使用查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29580037/

相关文章:

android - phonegap html5 android 同步文件系统 IO

android - 如何将我的应用程序的帐户添加到 Android 联系人应用程序的 "Accounts"列表?

javascript - 更好地从 Node 或每个请求的连接到 memcached/mongo 的单一全局连接?

sql - 如何选择数据库?

java - 如何在android中使用java在mysql数据库中保存值

java - 如何在不破坏和重新创建 fragment 的情况下在 fragment 之间切换? (以类似静态的方式)

sql - 当您达到 SQL Server Express 4GB/10GB 限制时会发生什么?

sqlite - SQLite 如何通过延迟事务防止死锁?

sql - 将父级与多个子级连接后合并重复的 SQLite 列

python - 接口(interface)错误: Error binding parameter 0 - probably unsupported type when runing my django script