Android アプリに既存のものを含める必要がありSQLiteDatabase
、新しいデータベースをダウンロードしてインストールする機能も必要です。私はいくつかの調査を行い、私の最初の実用的な解決策はhere から来ました。私はその解決策が好きではありませんでした.1つには、データベースが常に固定パスにあり、その他の奇妙な点があることを前提としています.
そこで、既存のデータベース ファイルをアセットに入れるのではなく、データベースを SQL ファイルにエクスポートして読み込み、onCreate()
my のメソッドで、ファイル データを使用して openでSQLiteOpenHelper
新しいメソッドを呼び出し ます。解決したと思ういくつかの問題に遭遇しましたが、すべての問題について考えていなかったと確信しています。updateDatabase
DataInputStream
SQLiteOpenHelper
のメソッドが呼び出されるとonCreate
、データベースが作成され、開かれinTransaction()
、true
. その結果、インポートされた sql ファイルに が含まれBEGIN TRANSACTION
ている場合は例外がスローされ、sql 文字列にステートメントが含まれている場合は'android_metadata'
さらに別の例外が作成されます。そこで、String.contains()
これらのキーワードを検索する単純な検索を追加し、それらを実行しないように を設定boolean
doExecute
しました。false
質問は、これをフィルタリングするためのより良い SQL クラスまたはメソッド、あるいはより良い正規表現メソッドはありますか?SQL ファイルに予期しない改行があるという同様の問題。とでファイルを読み、
readLine()
改行を探します。単純String.trim()
にその行で使用し、endsWith(";")
. これにより、1 行に複数のステートメントがないなど、入力ファイルにいくつかの制約が課されます。では、ファイルから 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;
}