使用事务的 Android SQLite 批量插入

Posted

技术标签:

【中文标题】使用事务的 Android SQLite 批量插入【英文标题】:Android SQLite Bulk Insert Using Transactions 【发布时间】:2015-11-23 07:16:26 【问题描述】:

我有一个插入语句,我的应用大约需要 25 秒才能插入约 1000 行。我正在尝试使用Transactions 来加快我的处理时间,但我无法实现它。

这是我在DatabaseHandler 中对inserting 的方法:

public boolean insertQuestions(String gid, String qid, String qname) 
    SQLiteDatabase db = this.getWritableDatabase();
    db.beginTransaction();
    try 
        ContentValues contentValues = new ContentValues();
        contentValues.put(KEY_GID, gid);
        contentValues.put(KEY_QID, qid);
        contentValues.put(KEY_QNAME, qname);

        long result = db.insert(TABLE_QUESTIONS, null, contentValues);
        db.setTransactionSuccessful();
        if (result == -1) 
            db.close();
            return false;
         else 
            db.close();
            return true;
        
     finally 
        db.endTransaction(); //Error is here
    

当我尝试运行上述方法时出现以下错误:

08-28 15:50:40.961  20539-20539/? E/androidRuntime﹕ FATAL EXCEPTION: main
Process: com.murrion.navigationdrawer, PID: 20539
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /storage/emulated/0/testapp3.db
        at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
        at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:522)
        at com.murrion.navigationdrawer.utils.DatabaseHandler.insertQuestions(DatabaseHandler.java:197)

该语句正在我的 Activity 中的 Asynctask 方法中运行。

【问题讨论】:

我认为当 db 在几行之前关闭时,您正试图在 db 中执行订单。你不能调用 db.endTransaction();调用后 db.close();尝试将 if/else 放在 db.endTransaction() 之后并告诉我们。希望对你有帮助 抱歉,我的措辞不好。它在一个循环中,所以它被执行了大约 1000 次。非常低效..我知道 【参考方案1】:

您在结束事务之前已经关闭了数据库,而是在结束后关闭它。

public boolean insertQuestions(String gid, String qid, String qname) 
    SQLiteDatabase db = this.getWritableDatabase();

    boolean wasSuccess = true;
    try 
        db.beginTransaction();
        ContentValues contentValues = new ContentValues();
        contentValues.put(KEY_GID, gid);
        contentValues.put(KEY_QID, qid);
        contentValues.put(KEY_QNAME, qname);

        long result = db.insert(TABLE_QUESTIONS, null, contentValues);

        if (result == -1) 
            wasSuccess = false;
         else 
            db.setTransactionSuccessful();
        
     finally 
        db.endTransaction(); 
        db.close();
    
    return wasSuccess;

【讨论】:

我进行了更改,代码正常运行。但是我的插入仍然需要大约 25 秒?这可能是我的平板电脑(Galaxy Tab 3 10.1")的限制吗?当我在 Genymotion 模拟器上运行它时,大约需要 8 秒。 @Mark117 你真的应该每行插入一个。并尝试指示您的领域! @Mark117 如果您真的想加快插入速度,ContentValues + db.insert + transactions 不会像准备好的语句那样有帮助。我的回答只解决了你的崩溃问题。请参阅此答案(Section Inser Valuess),因为加速插入已经(令人惊讶地)回答了***.com/a/29797229/794088(如果您发现任何有用的信息,请不要忘记投票=)【参考方案2】:

两个问题

    每个事务只执行一次插入。您需要在同一事务中执行所有插入操作。 您将在结束事务之前关闭数据库。

您可以创建一个Question 类来封装单个问题的数据。在这种情况下,您的方法可能如下所示:

public boolean insertQuestions(List<Question> questions) 
    SQLiteDatabase db = this.getWritableDatabase();
    db.beginTransaction();
    try 
        ContentValues contentValues = new ContentValues();
        for (Question question : Questions) 
            contentValues.put(KEY_GID, question.gid);
            contentValues.put(KEY_QID, question.qid);
            contentValues.put(KEY_QNAME, question.qname);
            db.insert(TABLE_QUESTIONS, null, contentValues);
        
        db.setTransactionSuccessful();
     finally 
        db.endTransaction();
    

【讨论】:

我认为这是我必须要做的。我目前在 for 循环中执行相同的 insertQuestions 方法〜 1000 次。传递列表似乎是解决这个问题的方法。谢谢【参考方案3】:

我认为您正在逐一使用insertQuestions() 插入问题。所以beginTransaction()endTransaction() 调用了很多次,效率很低。

需要进行一些更改以加快交易速度:

您应该首先将所有问题插入数组,然后使用如下:

db.beginTransaction();
for (entry : listOfQuestions) 
    db.insertQuestions("","","");

db.setTransactionSuccessful();
db.endTransaction();

对您的方法进行了一些修改。

public boolean insertQuestions(String gid, String qid, String qname) 
    SQLiteDatabase db = this.getWritableDatabase();

    boolean wasSuccess = true;
    try 
        db.beginTransaction();
        ContentValues contentValues = new ContentValues();
        contentValues.put(KEY_GID, gid);
        contentValues.put(KEY_QID, qid);
        contentValues.put(KEY_QNAME, qname);

        long result = db.insert(TABLE_QUESTIONS, null, contentValues);

        if (result == -1) 
            wasSuccess = false;
         else 
            db.setTransactionSuccessful();
        
     finally 
        db.endTransaction(); 
        db.close();
    
    return wasSuccess;

希望对你有帮助!

【讨论】:

以上是关于使用事务的 Android SQLite 批量插入的主要内容,如果未能解决你的问题,请参考以下文章

android sqlite禁止重复插入数据

SQLite数据库如何批量插入数据?

SQLite在使用参数和事务时不插入多于一行

Android Sqlite 批量插入性能优化

即使有事务,SQLite 插入也会变慢

android SQLite 批量插入数据慢的解决方案 (针对于不同的android api 版本)