3

我需要SQLiteDatabase在我的 Android 应用程序中包含一个现有的,并且我还希望能够下载和安装一个新的数据库。我做了一些研究,我的第一个工作解决方案来自这里。我不喜欢那个解决方案,一方面,它假设数据库总是在一个固定的路径和其他奇怪的地方。

因此,我没有将现有的数据库文件放在资产中,而是将数据库导出到 SQL 文件,将其读入,然后在onCreate()my 的方法中,我用文件数据SQLiteOpenHelper调用一个新方法 。我遇到了一些问题,我认为我已经解决了,但我确信我没有想到所有问题,它们是:updateDatabaseDataInputStream

  1. SQLiteOpenHelper'onCreate方法被调用时,数据库已经创建,它是打开的,并且inTransaction()true。因此,如果导入的 sql 文件包含BEGIN TRANSACTION,则会引发异常,并且如果 sql 字符串包含创建'android_metadata'另一个异常的语句。因此,我添加了一个使用String.contains()查找这些关键字的简单搜索,并设置了一个boolean doExecutefalse避免执行它们。所以一个问题是,是否有更好的 SQL 类或方法来过滤它,甚至是更好的正则表达式方法?

  2. SQL 文件中出现意外换行符的类似问题。我用 和 读取文件readLine()以查找换行符,我只是String.trim()在行上使用,然后检查endsWith(";"). 这对我的输入文件施加了一些限制,例如在一行中没有多个语句。那么,有没有更好的方法从文件中预处理 SQL 呢?

这是我DataInputStreamassets资源或下载中获得后用于创建数据库的代码:

    public boolean updateDatabase(DataInputStream inStream, SQLiteDatabase db, boolean doClear) throws Error {
    String sqlStatement = null;
    boolean result = true;
    boolean inOnCreate = true;
    boolean wasInTransaction;

    if(doClear) dropDatabase();

    // if called from onCreate() db is open and inTransaction, else getWritableDatabase()
    if(db == null) {
        inOnCreate = false;
        db = this.getWritableDatabase();
    }

    wasInTransaction = db.inTransaction();  // see NB below

    boolean doExecute;
    try {
        while ((sqlStatement = inStream.readLine()) != null) {
            // trim, so we can look for ';'
            sqlStatement.trim();
            if(!sqlStatement.endsWith(";")) {
                continue;   // line breaks in file, get whole statement
            }

            // NB - my file (exported from SQLite Database Browser starts with "BEGIN TRANSACTION;". 
            // executing this throws SQLiteException: cannot start a transaction within a transaction
            // According to SQLiteDatabase doc for beginTransaction(), "Transactions can be nested"
            // so this is a problem
            // but... possibly it is an "exclusive transaction" ?
            doExecute = true;
            if(wasInTransaction) {
                // don't execute BEGIN TRANSACTION; or COMMIT;
                if((sqlStatement.contains("BEGIN" ) || sqlStatement.contains("begin" )) &&
                        (sqlStatement. contains("TRANSACTION") || sqlStatement.contains("transaction" ))) {
                    doExecute = false;
                }
                if(sqlStatement.contains("COMMIT") || sqlStatement.contains("commit")) {
                    doExecute = false;
                }
            }   // inTransaction
            // this statement could be in older databases, but this scheme doesn't need, can't have it
            if(sqlStatement.contains("android_metadata")) {
                doExecute = false;
            }
            if(doExecute) {
                try {
                    db.execSQL(sqlStatement);
                } catch (SQLException e) {
                    throw(new Error("Error executing SQL " + sqlStatement));
                }   // try/catch
            }   // doExecute
        }   // while()
    } catch (IOException e) {
        result = false; // which won't matter if we throw 
        throw(new Error("Error reading  " + DB_SQL));
    } 

    if(!inOnCreate) {
        db.close();
    }

    return result;
}
4

1 回答 1

1

不想让您在如此雄心勃勃且优雅的实现中早早退出,但是如果您已经创建了数据库并使用数据库浏览器进行了检查,那么您是否考虑过SQLite Asset Helper?如果您的主要问题是被迫使用资产文件夹,则此方法lib可让您使用任何指定目录中的文件。此外,它允许处理原始.db文件。值得一试。

于 2012-11-18T00:57:38.100 回答