我需要SQLiteDatabase
在我的 Android 应用程序中包含一个现有的,并且我还希望能够下载和安装一个新的数据库。我做了一些研究,我的第一个工作解决方案来自这里。我不喜欢那个解决方案,一方面,它假设数据库总是在一个固定的路径和其他奇怪的地方。
因此,我没有将现有的数据库文件放在资产中,而是将数据库导出到 SQL 文件,将其读入,然后在onCreate()
my 的方法中,我用文件数据SQLiteOpenHelper
调用一个新方法 。我遇到了一些问题,我认为我已经解决了,但我确信我没有想到所有问题,它们是:updateDatabase
DataInputStream
当
SQLiteOpenHelper
'onCreate
方法被调用时,数据库已经创建,它是打开的,并且inTransaction()
是true
。因此,如果导入的 sql 文件包含BEGIN TRANSACTION
,则会引发异常,并且如果 sql 字符串包含创建'android_metadata'
另一个异常的语句。因此,我添加了一个使用String.contains()
查找这些关键字的简单搜索,并设置了一个boolean
doExecute
来false
避免执行它们。所以一个问题是,是否有更好的 SQL 类或方法来过滤它,甚至是更好的正则表达式方法?SQL 文件中出现意外换行符的类似问题。我用 和 读取文件
readLine()
以查找换行符,我只是String.trim()
在行上使用,然后检查endsWith(";")
. 这对我的输入文件施加了一些限制,例如在一行中没有多个语句。那么,有没有更好的方法从文件中预处理 SQL 呢?
这是我DataInputStream
从assets
资源或下载中获得后用于创建数据库的代码:
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;
}