无法写入导入的 SQLite 数据库 Android

Posted

技术标签:

【中文标题】无法写入导入的 SQLite 数据库 Android【英文标题】:Can't write to imported SQLite database Android 【发布时间】:2021-10-06 17:52:12 【问题描述】:

我已将一个现有数据库导入我的应用程序的assets/databases 文件夹。问题是我无法写入或截断表 (DETELE FROM) - 只能读取。另外,我没有遇到任何异常。

我正在使用从其父活动传递给构造函数的上下文从适配器调用DatabaseAccess

(我可以看到在androidStudio的Database Inspector中打开了数据库)

DatabaseOpenHelper.java

public class DatabaseOpenHelper extends SQLiteAssetHelper 
    private static final String DATABASE_NAME = "Products.db";
    private static final int DATABASE_VERSION = 1;

    public DatabaseOpenHelper(Context context) 
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    

DatabaseAccess.java:

public class DatabaseAccess 

    private SQLiteOpenHelper openHelper;
    private SQLiteDatabase database;
    private static DatabaseAccess instance;

    private DatabaseAccess(Context context) 
        this.openHelper = new DatabaseOpenHelper(context);
    

    public static DatabaseAccess getInstance(Context context) 
        if (instance == null) 
            instance = new DatabaseAccess(context);
        
        return instance;
    

    public void openDatabase() 
        this.database = openHelper.getWritableDatabase();
    

    public SQLiteDatabase getWritableDatabase()
        return openHelper.getWritableDatabase();
    

    public SQLiteDatabase getReadableDatabase() 
        return openHelper.getReadableDatabase();
    

    public void closeDatabase() 
        if (database != null) 
            this.database.close();
        
    

    public void insert(CartItem item) 
        try 
            String query = String.format(Locale.ENGLISH,"INSERT INTO main.CART_CONTENT (ITEM_DESCRIPTION, PRODUCER, UNIT_PRICE, QUANTITY) VALUES (\"%s\", \"%s\", %.2f, %.2f);",
                    item.getName(),
                    item.getProducer(),
                    item.getUnit_price(),
                    item.getQuantity());
            if(database != null) 
                database.rawQuery(query, null);
            
        
        catch (Exception e) 
            e.printStackTrace();
        

    

    public CartItem checkForProduct(CartItem item) 
        String query = "SELECT * FROM main.CART_CONTENT " +
                "WHERE ITEM_DESCRIPTION = \"%s\" AND PRODUCER = \"%s\"";
        query = String.format(Locale.ENGLISH, query, item.getName(), item.getProducer());

        Cursor cursor = getReadableDatabase().rawQuery(query, null);

        CartItem updatedItem = new CartItem();
        if (cursor.getCount() != 0) 
            cursor.moveToFirst();
            updatedItem = new CartItem(
                    cursor.getString(1),
                    cursor.getString(2),
                    Double.parseDouble(cursor.getString(3) + item.getUnit_price()),
                    Double.parseDouble(cursor.getString(4)) + item.getQuantity());
            cursor.close();
            insert(updatedItem);
            return  updatedItem;
        
        else 
            insert(item);
            return item;
        
    

    public void emptyDatabase() 
        try 
            database.rawQuery("DELETE FROM CART_CONTENT", null).close();
            database.rawQuery("DELETE FROM sqlite_sequence WHERE name='CART_CONTENT';", null).close();
        
        catch (Exception e) 
            e.printStackTrace();
        
    

【问题讨论】:

【参考方案1】:

另外,我没有遇到任何异常。

那是因为你在 try .... catch 中捕获异常,这是不推荐用于 SQLite 访问的东西(因为它隐藏了诸如语法错误之类的错误)。我建议如果您查看日志,您会发现被捕获的异常。

插入肯定会出现问题,因为它似乎忽略了:-

字符串常量由单引号(')括起来。字符串中的单引号可以通过将两个单引号放在一行中进行编码 - 就像在 Pascal 中一样。 不支持使用反斜杠字符的 C 样式转义,因为它们不是标准 SQL。 SQLite SQL Language Expressions - 3. Literal Values (Constants)

我建议使用 SQLiteDatabase 便捷方法而不是 rawQuery 方法。这些构建底层 SQL,绑定值。

参见insert(注意insertOrThrow 和insertOnConflict)和delete

所以或许可以考虑将 DatabaseAccess 中的 insertemptyDatabase 方法替换为:-

public long insert(CartItem item) 
    ContentValues cv = new ContentValues();
    cv.put("ITEM_DESCRIPTION",item.getName());
    cv.put("PRODUCER",item.getProducer());
    cv.put("UNIT_PRICE",item.getUnit_price());
    cv.put("QUANTITY",item.getQuantity());
    return database.insert("CART_CONTENT",null,cv);


public int emptyDatabase() 
    int rv;
    database.beginTransaction();
    rv = database.delete("CART_CONTENT",null,null);
    database.delete("sqlite_sequence","name=?",new String[]"CART_CONTENT");
    database.setTransactionSuccessful();
    database.endTransaction();
    return rv;

注意插入是INSERT OR IGNORE insert 返回插入行的 rowid(也就是具有 AUTOINCREMENT 编码的列,因此是 rowid 列的别名) 附注AUTOINCREMENT 很有可能只是在浪费资源 - 请参阅 SQLiteAutoincrement。但是,请注意,如果排除 AUTOINCREMENT,则不需要从 sqlite_sequenece 中删除该行,如果 CART_CONTENT 是唯一具有 AUTOINCREMENT 的表,则会导致找不到表失败。 emptyDatabase 返回从 CART_CONTENT 表中删除的行数。 有意省略了 try .... catch 块/子句。 SO上的很多SQLite问题因为使用了try .... catch而没有实现。 beginTransaction、setTransctionSuccessful、endTransaction 不是必需的,如果省略 AUTOINCREMENT,也没有任何用处。

请注意以上代码为in-principal code,尚未编译或运行,可能包含小错误

【讨论】:

以上是关于无法写入导入的 SQLite 数据库 Android的主要内容,如果未能解决你的问题,请参考以下文章

无法打开在 Android 中创建的 SQLite DB(文件已加密或不是数据库)

Sqlite大数据写入性能优化

android sqlite 的导入( import)问题

将 SQLite3 转储导入数据库

使用 sqlite、flutter app 导入数据库

Sqlite3 奇怪的行为