rawQuery有时无法在数据库中找到该项(多线程问题)

时间:2017-07-19 10:40:43

标签: android sqlite

我遇到了rawQuery的问题。有时候它没有按照ID找到一个项目,而有时却没有。

我的逻辑如下:我从Web服务中检索一些数据。我将它与本地数据库同步,然后我从本地数据库(而不是从我从Web服务收到的内容)将响应返回给用户。

我的问题是,当我同步数据时,rawQuery会从本地数据库中检索项目,看看我是否已经拥有它。如果它找不到它,它会插入一个新行,如果有,那么它只是更新它。这是方法:

@Override
public ShopItem syncShop(ShopItem remoteShop) {
    SQLiteDatabase db = DbManager.instance().openDatabase();

    Cursor c = db.rawQuery(
            DBContract.SHOPS.GET_SHOP_BY_REMOTE_ID,
            new String[]{String.valueOf(remoteShop.remote_id)});

    if (c.moveToFirst()) {//Item(s) exist
        ShopItem localShop = new ShopItem(c);
        if (localShop.updateDate == null || !localShop.updateDate.equals(remoteShop.updateDate)) {
            localShop.sync(remoteShop);

            db.update(DBContract.SHOPS.TABLE_NAME, localShop.getDbContentValues(),
                    DBContract.SHOPS.REMOTE_OBJ_ID + " = ?", new String[]{localShop.remote_id});
        }
        remoteShop = localShop;
    } else {//No item(s) found
        ContentValues values = remoteShop.getDbContentValues();
        long rowId = db.insert(DBContract.SHOPS.TABLE_NAME, null, values);

        if (rowId != -1) {
            remoteShop.local_id = String.valueOf(rowId);
        }

    }
    free(db, c);
    return remoteShop;
}

我尝试连续20次连续调用此方法(仅用于调试目的),其中有些时间c.moveToFirst()为false,因此它会插入一个新项而不是更新它。

在UI线程上,20个调用正常工作,他们不会创建新项目...但是我最近将上述方法的调用移动到了非UI线程,并且#39从那以后它就开始表现了。我100%肯定它是一个多线程的问题,但我无法弄清楚问题出在哪里。

这里是DbManager

/*package*/ class DbManager {

private int mOpenCounter;

private static DbManager mInstance;
private static DbHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;

public static synchronized void initializeInstance(DbHelper helper) {
    if (mInstance == null) {
        mInstance = new DbManager();
        mDatabaseHelper = helper;
    }
}

public static synchronized DbManager instance() {
    if (mInstance == null) {
        throw new IllegalStateException(DbManager.class.getSimpleName() +
                " is not initialized, call initializeInstance(..) method first.");
    }

    return mInstance;
}

public synchronized SQLiteDatabase openDatabase() {
    mOpenCounter++;
    if (mOpenCounter == 1) {
        // Opening new database
        mDatabase = mDatabaseHelper.getWritableDatabase();
    }
    return mDatabase;
}

public synchronized void closeDatabase() {
    mOpenCounter--;
    if (mOpenCounter == 0) {
        // Closing database
        mDatabase.close();

    }
}

}

有谁知道问题可能是什么?

干杯!

2 个答案:

答案 0 :(得分:1)

您绝不应该使用来自多个线程的相同SQLiteDatabase对象,因为一个连接只能有一个事务。

此外,您必须将query / update / insert调用包装到单个事务中,以防止其他线程在其间修改数据库。

答案 1 :(得分:0)

呀。所以这不是一个多线程问题。嗯,确实如此,但事实并非如此。

我有这个问题:

    static final String GET_VISIT_BY_REMOTE_ID = "SELECT " + TABLE_NAME + ".*" + ", " +
            SHOPS.TABLE_NAME + "." + SHOPS.NAME + " AS " + SHOPS.TABLE_NAME + "_" + SHOPS.NAME +
            " FROM " + TABLE_NAME +
            " INNER JOIN " + SHOPS.TABLE_NAME + " ON " + TABLE_NAME + "." + REMOTE_SHOP_ID + "=" + SHOPS.TABLE_NAME + "." + SHOPS.REMOTE_OBJ_ID +
            " WHERE " + TABLE_NAME + "." + REMOTE_OBJ_ID + " = ?";

问题在于,当上述查询正在运行时,另一个同步商店的查询也在运行,实际上是多个查询。一个清理了商店,另一个清理了数据。所以,有时候,即使delete + sync_shop查询紧跟在一起,处于不同的线程中,删除查询将首先运行,然后是上面的查询(没有查找商店,所以查询失败,即使它有访问返回),只有这样它才会重新填充商店的数据。

基本上我是删除数据,检查它是否存在(来自另一个线程)然后填充数据,但是由于另一个线程已经完成,所以太晚了。