【问题标题】:How to make multiple calls to SQLiteOpenHelper as a single atomic transaction?如何多次调用 SQLiteOpenHelper 作为单个原子事务?
【发布时间】:2012-12-24 13:30:06
【问题描述】:

我有一个SQLiteOpenHelper 类,它实现了单例模式并具有三个方法:addLogs()getLogs()deleteLogs()。七个线程(由 BroadcastReceiver 在某些调度下触发)访问数据库——其中六个只使用addLogs(),另一个使用getLogs() 并在完成之前调用deleteLogs()

我的问题是,因为最后一个线程对 SQLite 数据库进行了两次不同的调用,所以其他线程甚至可以在调用 deleteLogs() 之前调用 addLogs(),这会破坏我的输出。如何让一个线程将两个对 SQLiteOpenHelper 的调用视为单个原子事务?

我尝试过:在SQLiteOpenHelper 中创建一个静态ReentrantLock 成员,并在事务前后从七个线程中的每个线程调用lock()unlock()。即使最后一个线程仍然拥有锁,其他六个线程仍然可以通过,它在getLogs() 之前调用并在deleteLogs() 之后释放。

更新:这是访问这两种方法的第七个线程。

public class Uploader extends Thread {

    private Context context;

    public Uploader(Context context) {
        this.context = context;
    }

    @Override
    public void run() {
        Logger.d("STARTED: Uploader");
        DB db = DB.getInstance(context);
        List<JsonLog> logs = db.getLogs(); // FIRST CALL
        String json = new Gson().toJson(logs); // convert to JSON

        // connect to the server and upload
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(This.URL);
        post.setHeader("content-type", "application/json");
        try {
            Logger.d("to upload: " + json);
            StringEntity se = new StringEntity(json);
            post.setEntity(se);
            StatusLine status = client.execute(post).getStatusLine();
            // if upload is successful
            if (status.getStatusCode() == HttpStatus.SC_OK) {
                db.deleteLogs(); // SECOND CALL
            } else {
                Logger.d("Uploader failed. " + status.getStatusCode() + ": " + status.getReasonPhrase());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Logger.d("FINISHED: Uploader");
    }

}

【问题讨论】:

    标签: java android multithreading sqlite concurrency


    【解决方案1】:

    SQLite 有db.beginTransaction()db.setTransactionSuccessful() 中的事务,用于标记要提交的 Tx,db.endTransaction() 最终将 Tx 标记为完成。

    因此,您可以在 getLogs() 中执行 beginTransaction 并在 deleteLogs 中将其标记为已完成(并且成功)。

    [更新] 现在,使用您的代码 sn-p,您可能希望在第一次调用之前调用 beginTransaction,并在 finally 块中调用,这会将 try-catch 块调用添加到 endTransaction。在try 部分结束时,如果状态正常,您将调用setTransactionSuccessful(在delete 调用之后。

    【讨论】:

    • 哇哦,太酷了。但是无论如何我可以从线程的run() 方法中做类似的事情,因此作为SQLiteOpenHelper 对象?恐怕我可能会添加另一个线程,分别调用getLogs()deleteLogs()
    • 我更新了我的问题以包含第七个线程的代码,即Uploader。我希望在第一次调用之前调用beginTransaction 之类的东西,并在整个上传过程完成后调用setTransactionSuccessfulendTransaction
    猜你喜欢
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 1970-01-01
    • 2016-06-23
    • 1970-01-01
    • 2013-11-30
    • 1970-01-01
    • 2018-06-18
    相关资源
    最近更新 更多