【问题标题】:What is the correct way to do inserts/updates/deletes in Android SQLiteDatabase using a query string?使用查询字符串在 Android SQLiteDatabase 中插入/更新/删除的正确方法是什么?
【发布时间】:2013-11-21 00:45:47
【问题描述】:

我一直在查看官方文档 (http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html) 并与 StackOverflow 帖子和实际观察到的行为进行交叉引用,并且该文档似乎具有误导性,并且在几个方面可能只是错误的。

我正在尝试执行参数化查询以插入/更新/删除,就像使用rawQuery(String sql, String[] arguments). 进行选择一样,我需要通过查询而不是使用 SQLiteDatabase 上的插入/更新/删除方法来执行此操作类,因为在许多情况下,查询跨越多个表,并且在任何情况下,我们都编写了一大组查询,用于我们所有平台上的这个应用程序(也为 SQLite 编写),并且最好将它用作 -是。

以下是我认为令人困惑/误导的内容:

  • 文档说 rawQuery 的语句必须以分号结尾,但实际上似乎没有任何区别。这对我来说很重要,因为我们有一个巨大的 XML 文档,其中填充了我在多个平台的应用中使用的查询,如果可能的话,我希望保持相同。

  • rawQuery 似乎不适用于插入(是的,我尝试过使用和不使用分号)。医生没有说这件事。我确实看到它返回了一个游标,我想这可能是一个暗示,它只适用于一个选择,但不一定——当它们没有结果集时,它可以简单地返回一个零长度或空游标。

  • execSQL(String sql, Object[] bindArgs) 明确表示它确实适用于插入,但实际上它可以!

  • 此外,尽管execSQL(String, Object[]) 明确告诉您不要尝试 CRUD 操作,但其无参数版本不包含此类警告,并且也可以正常工作(缺点是不允许使用 SQL 参数)。

所有这一切的结果是,我能找到成功执行带有参数化参数的插入的唯一方法是使用文档明确指示您使用的一种工作方法为此目的。

另外,rawQuery 不能用于插入/更新/删除,这真是令人遗憾,因为我们有一个通用的数据层,如果我们可以使用一个统一的 API 调用来运行我们所有的 CRUD,那就更优雅了查询数据库。

那么这里发生了什么?该文档是否已经过时了?用 execSql 做插入、更新等可以吗?有没有人成功使用 rawQuery 进行插入?


附录:

针对操作系统运行:Android 4.3。

示例查询(什么有效,什么无效)

// Works
db.execSql("INSERT into MyTable (Col1, Col2) VALUES (?, ?)", new String[] { "Foo", "Bar" });
db.execSql("INSERT into MyTable (Col1, Col2) VALUES (?, ?);", new String[] { "Foo", "Bar" });

// No exception thrown, but no changes made to the database
db.rawQuery("INSERT into MyTable (Col1, Col2) VALUES (?, ?)", new String[] { "Foo", "Bar" });
db.rawQuery("INSERT into MyTable (Col1, Col2) VALUES (?, ?);", new String[] { "Foo", "Bar" });

【问题讨论】:

  • 为什么你认为rawQuery 可以用于非查询操作?查询特指SELECT 操作
  • @ianhanniballake,根据我的经验,“查询”通常用于指代任何准备发送到数据库的 SQL 语句,无论它是否是 SELECT。毕竟,它被称为结构化查询语言。所以说“更新查询”是很常见的。这是使用此措辞的维基百科文章(搜索“查询”):en.wikipedia.org/wiki/Update_%28SQL%29
  • 我问是因为 Android 文档非常清楚,查询仅指 SELECT,所以我不确定是什么 Android 文档让您误入歧途?
  • 也许我正在阅读错误的文档,但我在发布的链接中没有看到任何内容表明您只能在 rawQuery 中运行 SELECT。相反,它明确表示您不能在 execSQL 中执行 CRUD 操作。那么你还会在哪里做呢?
  • 您好,如何知道插入成功?

标签: java android sql sqlite


【解决方案1】:

你是对的。文档令人困惑。总的来说,该设计试图围绕 sqlite3 C API 提供一个方便的 Java 包装器。在大多数情况下,如果您按照设计师的意图使用它,它就会正常工作,例如使用方便的方法进行 CRUD 操作。但他们还需要提供原始查询方法execSQL()rawQuery(),以应对便利的CRUD 方法不够强大或根本不适用的情况(CREATE TABLE 等)。这会导致抽象泄漏。

文档说rawQuery 的语句不能以分号结尾,但实际上似乎没有任何区别。这对我来说很重要,因为我们有一个巨大的 XML 文档,其中填充了我在多个平台的应用程序中使用的查询,如果可能的话,我更愿意保持相同。

文档很糟糕。其实AndroidSQLiteDatabase本身calls rawQuery with a semicolon-terminated query string

rawQuery 似乎不适用于插入(是的,我尝试过使用和不使用分号)。医生没有说这件事。我确实看到它返回了一个游标,我想这可能是一个暗示,它只适用于一个选择,但不一定——当它们不是结果集时,它可以简单地返回一个零长度或空游标。

确实有效,但您需要了解它在原生级别上的工作原理。

execSQL() 视为运行查询并返回成功或错误代码的sqlite3_exec()

rawQuery() 视为sqlite3_prepare() 编译查询但尚未运行它。要实际运行它,请使用Cursor 上的moveTo...() 方法之一。将此视为sqlite3_step()。将任何rawQuery()moveTo...() 结合起来实际上会改变数据库。

execSQL(String sql, Object[] bindArgs) 明确表示它不适用于选择,但实际上可以!

此外,尽管execSQL(String, Object[]) 明确告诉您不要尝试 CRUD 操作,但其无参数版本不包含此类警告,并且也可以正常工作(缺点是不允许使用 SQL 参数)。

它适用于所有 CRUD 操作。对于 CRUD 的 R 读取部分,无法获取所选数据。

【讨论】:

  • 感谢您提供全面而富有启发性的答案。我尝试使用rawQuery() 后跟moveToFirst() 进行插入,我认为这对我的数据层的一般消费者来说是安全的,并且成功了!我有一部分人担心在内部实现知识之上构建是危险的,但我认为无论我使用哪一个,我都会以某种方式忽略文档。 :-) 再次感谢!我相信这次讨论也会对其他人有所帮助。
  • 快速跟进任何想使用我的策略的人。一定是早上太早了......调用moveToFirst() 以便在将Cursor 返回给毫无戒心的消费者之前执行查询显然会导致他们错过第一行。但是,您可以调用moveToPosition(-1) 来执行查询,而不会影响游标在结果集上的位置。通过这种方式,您可以使用rawQuery(...) 进行任何类型的 CRUD 操作。
  • 重要跟进:当查询为 BEGIN ...、COMMIT ... 或 ROLLBACK 时,在 Android 4.0.3 和 4.0.4 上调用 rawQuery(...) 后跟 moveToPosition(-1) 会引发异常交易。对于这些查询,您应该改用execSQL
【解决方案2】:

您想要的是用于插入的 execSQL()。或者你可以这样做:

ContentValues values = new ContentValues();
values.put ("col1", "foo");
values.put ("cols2", "bar");
getDb().insertOrThrow ("MyTable", null, values);

对于查询,您可以使用 rawQuery。

【讨论】:

  • 所以听起来你在说文档是错误的(我完全同意——我只是在验证)。还是您仅指的是 execSQL() 的无参数版本?
  • @BrianRak 文档有误;它说的是设计师希望你做什么,而不是你需要做什么才能完成任务。
猜你喜欢
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2022-11-14
  • 2018-03-24
相关资源
最近更新 更多