在Android中预填充数据库的最快、最有效的方法
如果您想在Android中预填充数据库(SQLite),这并不像人们想象的那么容易 所以我发现这里也经常提到堆栈溢出 但是我不太喜欢这种预先填充数据库的方式,因为您从数据库处理程序获得控制权,然后自己创建文件。我宁愿不接触文件系统,让数据库处理程序自己完成所有事情 因此,我认为可以像往常一样在数据库处理程序的onCreate()中创建数据库,然后从/assets加载一个文件(.sql),其中包含用于填充值的语句:在Android中预填充数据库的最快、最有效的方法,android,sql,database,sqlite,Android,Sql,Database,Sqlite,如果您想在Android中预填充数据库(SQLite),这并不像人们想象的那么容易 所以我发现这里也经常提到堆栈溢出 但是我不太喜欢这种预先填充数据库的方式,因为您从数据库处理程序获得控制权,然后自己创建文件。我宁愿不接触文件系统,让数据库处理程序自己完成所有事情 因此,我认为可以像往常一样在数据库处理程序的onCreate()中创建数据库,然后从/assets加载一个文件(.sql),其中包含用于填充值的语句: INSERT INTO testTable (name, pet) VALUES
INSERT INTO testTable (name, pet) VALUES ('Mike', 'Tiger');
INSERT INTO testTable (name, pet) VALUES ('Tom', 'Cat');
...
但是在处理程序的onCreate()中调用execSQL()实际上并不起作用。似乎/assets文件的大小不能超过1MB,execSQL()只执行第一条语句(Mike-Tiger)
在预填充数据库之前,您会怎么做?是的,资产可能有大小限制,因此如果大于限制,您可以剪切到更多文件 exesql支持更多的sql语句,这里举个例子:
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(asManager.open(INIT_FILE)), 1024 * 4);
String line = null;
db.beginTransaction();
while ((line = br.readLine()) != null) {
db.execSQL(line);
}
db.setTransactionSuccessful();
} catch (IOException e) {
FLog.e(LOG_TAG, "read database init file error");
} finally {
db.endTransaction();
if (br != null) {
try {
br.close();
} catch (IOException e) {
FLog.e(LOG_TAG, "buffer reader close error");
}
}
}
上面的示例需要INIT_文件需要每一行都是sql语句
此外,如果您的sql语句文件很大,您可以在android的网站外创建数据库(sqlite支持windows、linux,因此您可以在操作系统中创建数据库,并将数据库文件复制到资产文件夹中,如果很大,您可以压缩它)
当应用程序运行时,您可以从资产中获取数据库文件,直接保存到应用程序的数据库文件夹中(如果对其进行压缩,则可以解压缩到应用程序的数据库文件夹)
希望可以帮助您-:您的问题表明,您想要最快的方式-但您不喜欢本文中的方式-您不想手动替换DB文件(尽管它实际上可能比用查询填充空DB更快) 我也有同样的想法——我发现,通过SQL语句填充和预填充都是最好的解决方案——但这取决于使用DB的方式 在我的应用程序中,我需要在第一次运行时在DB中有大约2600行(4列)——这是用于自动完成的数据和其他一些东西。它很少会被修改(用户可以添加自定义记录,但大多数情况下-他们不需要这样做),而且非常大。从SQL语句填充它不仅需要更多的时间,而且需要APK中更多的空间(假设我将数据存储在其中,或者可以从internet下载) 这是一个非常简单的例子(“大”插入只能发生一次,而且只能在第一次启动时发生),我决定继续复制预填充的DB文件。当然,这可能不是最好的方法,但它更快。我希望我的用户能够尽可能快地使用该应用程序,并将速度视为优先事项——他们真的很喜欢它。相反,我怀疑当应用程序速度变慢时,他们会高兴,因为我认为更慢更好的解决方案实际上更好 如果不是2600行,我的表最初会有50行,我会使用SQL语句,因为速度和大小的差异不会太大
你必须决定哪种解决方案更适合你的情况。如果您预见到使用“预填充db”选项可能会出现任何问题,请不要使用它。如果您不确定这些问题,请询问,提供有关如何使用(并最终升级)DB内容的更多详细信息。如果您不确定哪种解决方案会更快,请对其进行基准测试。不要害怕这种复制文件的方法——如果使用得当,它可以非常有效。我建议如下:
INSERT
逻辑包装到事务中(BEGIN…COMMIT
,或通过…API)另外看一看我写了一个类似于前面答案的DbUtils类。它是ORM工具的一部分,在上提供。不同之处在于,它将尝试使用一个简单的正则表达式(而不仅仅是行尾)查找语句边界。如果必须依赖SQL文件,我怀疑是否有更快的方法 但是,如果您可以以另一种格式提供数据,那么它应该比使用SQL脚本快得多。诀窍是使用一个新的方法。对于每个数据行,将解析后的值绑定到语句并执行该语句。当然,您需要在事务内部执行此操作。我建议使用一种简单的分隔符分隔文件格式(例如CSV),因为它的解析速度比XML或JSON快
我们为绿岛做了一些。对于我们的测试数据,我们的插入速率约为每秒5000行。出于某种原因,.我使用了这种方法。首先创建您的sqlite数据库,有几个程序可以使用,我喜欢。然后将数据库文件复制到资产文件夹中。然后可以在SQLiteOpenHelper的构造函数中使用此代码
final String outFileName = DB_PATH + NAME;
if(! new File(outFileName).exists()){
this.getWritableDatabase().close();
//Open your local db as the input stream
final InputStream myInput = ctx.getAssets().open(NAME, Context.MODE_PRIVATE);
//Open the empty db as the output stream
final OutputStream myOutput = new FileOutputStream(outFileName);
//final FileOutputStream myOutput = context.openFileOutput(outFileName, Context.MODE_PRIVATE);
//transfer bytes from the inputfile to the outputfile
final byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
((FileOutputStream) myOutput).getFD().sync();
myOutput.close();
myInput.close();
}
} catch (final Exception e) {
// TODO: handle exception
}
DB_路径类似于/data/data/com.mypackage.myapp/databases/
名称是您选择的任何数据库名称“mydatabase.db”
我知道这段代码有很多改进,但它工作得很好,速度也很快。所以我就不管了。这样在onCreate()方法中可能会更好。此外,每次检查文件是否存在可能不是最好的方法。不管怎么说,就像我说的那样,它工作迅速可靠。你可以吃蛋糕,也可以吃蛋糕。这里有一个解决方案,它既可以尊重db适配器的使用,也可以对预填充的数据库使用一个简单(且快得多)的复制过程 我正在使用一个基于Google示例之一的db适配器。它包括一个内部类dbHelper(),该类扩展了Android的SQLiteOpenHelper()类。诀窍是覆盖它的onCrea
@Override
public void onCreate(SQLiteDatabase db) {
mNeedToCopyDb = true;
}
private Boolean mNeedToCopyDb = false;
/**
* Open the database using the adapter. If it cannot be opened, try to
* create a new instance of the database. If it cannot be created,
* throw an exception to signal the failure.
*
* @return this (self reference, allowing this to be chained in an
* initialization call)
* @throws SQLException if the database could neither be opened nor created
*/
public MyDbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getReadableDatabase();
if (mDbHelper.mNeedToCopyDb == true){
mDbHelper.close();
try {
copyDatabase();
} catch (IOException e) {
e.printStackTrace();
} finally {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getReadableDatabase();
}
}
return this;
}
dbInputStream = mCtx.getResources().openRawResource(R.raw.mydatabase);