Skip to content

Files

Latest commit

1de56a1 · Dec 25, 2021

History

History
501 lines (339 loc) · 29.5 KB

File metadata and controls

501 lines (339 loc) · 29.5 KB

一、进入 SQLite

建筑师、《SQLite》的主要作者理查德·希普博士在 2007 年 6 月出版的《卫报》采访中解释了这一切是如何开始的:

“我从 2000 年 5 月 29 日开始。它刚刚超过七年,”他说。他正在做一个使用数据库服务器的项目,但数据库有时会离线。“然后我的程序会给出一条错误消息,说数据库不工作,我为此受到了责备。所以我说,这不是一个对数据库要求很高的应用,为什么我不直接和磁盘对话,这样构建一个 SQL 数据库引擎呢?事情就是这样开始的。”

在我们开始探索安卓环境下的 SQLite 之前,我们想告诉你一些先决条件。以下是非常基本的要求,不需要您做什么努力:

有了这些东西,我们现在可以开始进军 SQLite 了。

在本章中,我们将介绍以下内容:

  • 为什么是 SQLite?
  • SQLite 架构
  • 数据库基础的快速回顾
  • 安卓系统中的 SQLite

为什么是 SQLite?

SQLite 是一个嵌入式的 SQL 数据库引擎。在 Adobe 集成运行时(AIR)中由 Adobe 等知名名称使用;空中客车,在他们的飞行软件中;Python 附带 SQLitePHP 还有更多。在移动领域,SQLite 由于其轻量级的特性,在各种平台上都是非常受欢迎的选择。苹果在 iPhone 中使用,谷歌在安卓操作系统中使用。

它用作应用文件格式、电子小工具数据库、网站数据库以及企业关系数据库管理系统。是什么让 SQLite 成为这些公司和许多其他公司如此有趣的选择?让我们仔细看看 SQLite 的特性,这些特性使它如此受欢迎:

  • 零配置 : SQLite 的设计不需要配置文件。它不需要安装步骤或初始设置;它没有运行的服务器进程,即使崩溃也没有恢复步骤。没有服务器,它直接嵌入到我们的应用中。此外,不需要管理员来创建或维护数据库实例,或者为用户设置权限。简而言之,这是一个真正的无数据库管理员数据库。

  • 无版权 : SQLite 不是有许可证,而是有祝福。SQLite 的源代码在公共领域;您可以自由修改、分发甚至出售代码。甚至投稿人也被要求签署一份宣誓书,以防止未来可能发生的版权战争。

  • 跨平台:数据库文件可以从一个系统转移到运行不同架构的系统,没有任何麻烦。这是可能的,因为数据库文件格式是二进制的,所有的机器都使用相同的格式。在接下来的几章中,我们将把一个数据库从安卓模拟器中拉出到窗口中。

  • 压缩:一个 SQLite 数据库就是一个普通的单盘文件;它没有服务器,设计轻巧简单。这些属性导致了一个非常轻量级的数据库引擎。与其他规模更大的 SQL 数据库引擎相比,SQLite 3 . 7 . 8 版的内存不足 350 KiB (kibibyte)。

  • Fool proof: The code base is well commented, easy to understand, and modular. The test cases and test scripts in SQLite have approximately 1084 times more code than the source code of SQLite library and they claim 100 percent branch test coverage. This level of testing reaffirms the faith instilled in SQLite by developers.

    感兴趣的读者可以在http://en.wikipedia.org/wiki/Code_coverage的维基百科上阅读更多关于分支测试覆盖率的信息。

SQLite 架构

核心、SQL 编译器、后端和数据库构成了 SQLite 架构:

The SQLite architecture

SQlite 界面

根据文档,在 SQLite 库栈的顶部,SQLite 库的公共接口的大部分是由wen.clegacy.cvdbeapi.c源文件实现的。这是其他程序和脚本的通信点。

SQL 编译器

标记器将从接口传递的 SQL 字符串分成标记,并将标记一个接一个地交给解析器。令牌化器是用 c 语言手工编码的。SQLite 的解析器是由 Lemon 解析器生成器生成的。它比 YACC 和比索快,同时线程安全,防止内存泄漏。解析器根据标记器传递的标记构建解析树,并将树传递给代码生成器。生成器根据输入生成虚拟机代码,并将其作为可执行文件传递给虚拟机。更多关于柠檬解析器生成器的信息可以在http://en.wikipedia.org/wiki/Lemon_Parser_Generator找到。

虚拟机

虚拟机又称虚拟数据库引擎 ( VDBE )是 SQLite 的心脏。它负责获取和更改数据库中的值。它执行代码生成器生成的程序来操作数据库文件。每个 SQL 语句首先被转换成虚拟机语言用于 VDBE。VDBE 的每条指令包含一个操作码和最多三个附加操作数。

SQLite 后端

b 树与寻呼机和操作系统接口一起构成了 SQLite 架构的后端。B-trees 用于组织数据。另一方面,寻呼机通过缓存、修改和回滚数据来帮助 B 树。当需要时,b 树从高速缓存中请求特定的页;寻呼机以有效和可靠的方式处理这个请求。顾名思义,操作系统接口提供了一个抽象层来连接不同的操作系统。它对 SQLite 调用隐藏了与不同操作系统通信的不必要的细节,并代表 SQLite 处理它们。

这些都是 SQLite 的内部,安卓系统中的应用开发者不用担心安卓系统的内部,因为 SQLite 安卓库有效地利用了抽象的概念,所有的复杂性都被隐藏起来了。人们只需要掌握所提供的应用编程接口,这将满足安卓应用中所有可能的 SQLite 用例。

快速回顾数据库基础知识

简单地说,数据库是一种以连续方式存储数据的有组织的方式。数据保存在表格中。表由具有不同数据类型的列组成。表中的每一行都对应一条数据记录。你可以把一个表格想象成一个电子表格。从面向对象编程的角度来看,数据库中的每个表通常描述一个对象(由一个类表示)。每个表列都说明了一个类属性。表中的每条记录都代表该对象的一个特定实例。

让我们看一个简单的例子。假设您有一个Shop数据库,其中有一个名为Inventory的表。这个表可以用来存储商店里所有产品的信息。Inventory表可能包含这些列:Product name(字符串)、Product Id(数字)、Cost(数字)、In stock (0/1)和Numbers available(数字)。然后,您可以为名为Shoe的产品在数据库中添加一条记录:

|

身份证明

|

产品名称

|

产品标识

|

费用

|

有现货的

|

可用号码

| | --- | --- | --- | --- | --- | --- | | one | 地毯 | Three hundred and forty thousand and twenty-three | Two thousand three hundred and ten | one | four | | Two | 鞋 | Two hundred and thirty-one thousand two hundred and fifty-seven | Two hundred and thirty-five | one | Two |

数据库中的数据应该被检查和影响。表中的数据可以如下所示:

  • 添加了INSERT命令
  • 已修改(使用UPDATE命令)
  • 移除(使用DELETE命令)

您可以利用所谓的查询在数据库中搜索特定数据。一个查询(使用SELECT命令)可以涉及一个表或多个表。要生成查询,必须使用 SQL 命令确定感兴趣的表、数据列和值。每个 SQL 命令都以分号(;)结束。

什么是 SQLite 语句?

一个 SQLite 语句是用 SQL 编写的,它被发送到一个数据库,以检索数据或者在数据库中创建、插入、更新或删除数据。

所有 SQLite 语句都以任意关键字开始:SELECTINSERTUPDATEDELETEALTERDROP等,所有语句都以 a 分号(;)结束。例如:

CREATE TABLE table_name (column_name INTEGER);

CREATE TABLE命令用于在 SQLite 数据库中创建新表。一个CREATE TABLE命令描述了正在创建的新表的以下属性:

  • 新表的名称。
  • 创建新表的数据库。表可以在主数据库、临时数据库或任何附加的数据库中生成。
  • 表中每一列的名称。
  • 表中每列的声明类型。
  • 表中每列的默认值或表达式。
  • 每列使用的默认关系序列。
  • 最好是一张PRIMARY KEY为桌子。这将支持单列和复合(多列)主键。
  • 每个表的一组 SQL 约束。支持UNIQUENOT NULLCHECKFOREIGN KEY等约束。
  • 在某些情况下,该表将是WITHOUT ROWID表。

以下是创建表的简单 SQLite 语句:

String databaseTable =   "CREATE TABLE " 
   + TABLE_CONTACTS +"(" 
   + KEY_ID  
   + " INTEGER PRIMARY KEY,"
   + KEY_NAME + " TEXT,"
   + KEY_NUMBER + " INTEGER"
   + ")";

这里,CREATE TABLE是创建名为TABLE_CONTACTS的表的命令。KEY_IDKEY_NAMEKEY_NUMBER为列标识。SQLite 要求为每列提供唯一的标识。INTEGERTEXT是与相应列相关联的数据类型。SQLite 要求在创建表时定义要存储在列中的数据类型。PRIMARY KEY是数据列约束(表中数据列强制执行的规则)。

SQLite 支持更多可用于创建表的属性,例如,让我们创建一个create table语句,为空列输入默认值。请注意,对于KEY_NAME,我们提供的默认值为xyz,对于KEY_NUMBER列,我们提供的默认值为100:

String databaseTable = 
   "CREATE TABLE " 
   + TABLE_CONTACTS  + "(" 
   + KEY_ID    + " INTEGER PRIMARY KEY,"

   + KEY_NAME + " TEXT DEFAULT  xyz,"

   + KEY_NUMBER + " INTEGER DEFAULT 100" + ")";

这里,当一行被插入到数据库中时,这些列将用CREATE SQL 语句中定义的默认值进行预初始化。

关键词更多,但我们不希望你对一个庞大的列表感到厌烦。我们将在后续章节中介绍其他关键词。

SQlite 语法

SQLite 遵循一套独特的规则和指导方针,称为语法

需要注意的一个重要的点是 SQLite 是不区分大小写的,但是有一些命令是区分大小写的,比如说GLOBglob在 SQLite 里面的意思是不一样的。让我们看一下 SQLite DELETE语句的语法。虽然我们使用了大写字母,但是用小写字母替换它们也可以:

DELETE FROM table WHERE {condition};

SQlite 中的数据类型

SQLite 使用动态和弱类型的 SQL 语法,而大多数 SQL 数据库使用静态、严格的类型。如果我们看看其他语言,Java 是静态类型语言,Python 是动态类型语言。那么我们说动态或静态是什么意思呢?让我们看一个例子:

a=5
a="android"

在静态类型语言中,这将引发异常,而在动态类型语言中,这将有效。在 SQLite 中,值的数据类型与其容器无关,而是与值本身有关。在处理静态类型的系统时,这不是一个值得关注的问题,在静态类型的系统中,值是由容器决定的。这是因为 SQLite 向后兼容更常见的静态类型系统。因此,我们用于静态系统的 SQL 语句可以在这里无缝使用。

存储类别

在 SQLite 中,我们有比数据类型更通用的存储类。在内部,SQLite 将数据存储在五个存储类中,也可以称为原始数据类型:

  • NULL:这个代表数据库中缺少的值。
  • INTEGER:根据数值的大小,支持 1、2、3、4、6 或 8 字节的有符号整数范围。SQLite 根据该值自动处理。在内存中处理时,它们被转换为最通用的 8 字节有符号整数形式。
  • REAL:这个是一个浮点值,SQLite 用这个作为一个 8 字节的 IEEE 浮点数来存储这样的值。
  • TEXT : SQLite 支持多种字符编码,如 UTF-8、UTF-16BE 或 UTF-16LE。该值是一个文本字符串。
  • BLOB:这种类型存储了一大串二进制数据,确切地说是如何将其作为输入提供的。

SQLite 本身不验证写入列的类型是否实际上是定义的类型,例如,您可以将整数写入字符串列,反之亦然。我们甚至可以拥有一个包含不同存储类别的单个列:

 id                   col_t
------               ------
1                       23
2                     NULL
3                     test

布尔数据类型

SQLite 没有单独的布尔的存储类,为此使用Integer类。整数0代表假状态,而1代表真状态。这意味着间接支持布尔,我们只能创建布尔类型的列。抓住的是,它不会包含熟悉的TRUE / FALSE 价值观。

日期和时间数据类型

正如我们在布尔数据类型中看到的,在 T2 SQLite 中没有日期和时间数据类型的存储类。SQLite 有五个内置的日期和时间功能来帮助我们;我们可以使用日期和时间作为整数、文本或实数值。此外,根据应用的需要,这些值是可以互换的。例如,要计算当前日期,请使用以下代码:

SELECT date('now');

安卓中的 SQLite

Android 软件栈由核心 Linux 内核、Android 运行时、支持 Android 框架的 Android 库,最后是在万物之上运行的 Android 应用组成。安卓运行时使用达尔维克虚拟机 ( DVM )来执行 dex 代码。在较新版本的安卓系统中,也就是说,从 KitKat (4.4)开始,安卓系统启用了一个名为 ART 的实验特性,最终将取代 DVM。它基于提前 ( AOT ),而 DVM 基于及时 ( 准时制)。下图中,我们可以看到 SQLite 提供了原生数据库支持,是支持应用框架的库的一部分,还有 SSL、OpenGL ES、WebKit 等库。这些用 C/C++ 编写的库运行在 Linux 内核上,与 Android 运行时一起构成了应用框架的主干,如下图所示:

SQLite in Android

在我们开始探索安卓系统中的 SQLite 之前,我们先来看看安卓系统中的其他持久存储替代方案:

  • 共享首选项:数据以键值形式存储在共享首选项中。文件本身是一个包含键值对的 XML 文件。文件存在于应用的内部存储中,根据需要,对它的访问可以是公开的,也可以是私有的。Android 提供了编写和读取共享首选项的 API。建议在我们必须保存少量此类数据的情况下使用它。一个常见的例子是将最后一次阅读的位置保存在 PDF 中,或者保存用户的偏好以显示评级框。

  • Internal/external storage: This terminology can be a little misleading; Android defines two storage spaces to save files. On some devices, you might have an external storage device in form of an SD card, whereas on others, you will find that the system has partitioned its memory into two parts, to be labeled as internal and external. Paths to the external as well as internal storage can be fetched by using Android APIs. Internal storage, by default, is limited and accessible only to the application, whereas the external storage may or may not be available in case it is mounted.

    类型

    android:installLocation可以在清单中用于指定应用的内部/外部安装位置。

SQLite 版本

从 API 级开始,Android 就搭载了 SQLite。在撰写本书时,SQLite 的当前版本是 3.8.4.1。根据文档,SQLite 的版本是 3.4.0,但是已知不同的安卓版本会附带不同版本的 SQLite。我们可以通过使用 Android SDK 安装文件夹和 Android Emulator 内platform-tools文件夹中的名为 SQLite3 的工具轻松地验证这一点:

adb shell SQLite3 --version
SQLite 3.7.11: API 16 - 19
SQLite 3.7.4: API 11 - 15
SQLite 3.6.22: API 8 - 10
SQLite 3.5.9: API 3 - 7

我们不用担心 SQLite 的不同版本,为了兼容性应该坚持 3.5.9,或者我们可以说 API 14 是新的minSdkVersion,换成 3.7.4。除非你对某个特定的版本有非常具体的东西,否则这并不重要。

一些额外的便捷的 SQLite3 命令如下:

  • .dump:打印出表格的内容
  • .schema:打印现有表格的语句
  • .help:用于说明

数据库包

android.database包包含了所有使用数据库的必要类。 android.database.SQLite包包含特定于 SQLite 的类。

API

安卓提供了各种 API,让我们可以创建、访问、修改和删除数据库。完整的清单可能会让人不知所措;为了简洁起见,我们将介绍最重要和最常用的。

SQliteOpenHelper 类

SQLiteOpenHelper类是安卓第一个也是最基本的使用 SQLite 数据库的类;它存在于android.database.SQLite命名空间中。SQLiteOpenHelper是一个助手类,是为扩展而设计的,用于在创建、打开和使用数据库时实现您认为重要的任务和操作。这个助手类是由安卓框架提供的,用于处理 SQLite 数据库,并帮助管理数据库创建和版本管理。工作方式是扩展类,并按照我们的应用的要求实现任务和操作。SQLiteOpenHelper具有如下定义的构造函数:

SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler)

应用上下文允许访问应用的所有共享资源和素材。name参数由安卓存储中的数据库文件名组成。SQLiteDatabase.CursorFactory是一个工厂类,它创建光标对象,这些对象充当您在 Android 下对 SQLite 应用的所有查询的输出集。数据库的特定于应用的版本号将是版本参数(或者更具体地说,它的模式)。

SQLiteOpenHelper的构造函数用于创建一个帮助对象,以创建、打开或管理数据库。 上下文是允许访问所有共享资源和素材的应用上下文。name参数或者包含数据库的名称,或者对于内存数据库为空。SQLiteDatabase.CursorFactory工厂创建一个光标对象,作为所有查询的结果集。version参数定义数据库的版本号,用于数据库的升级/降级。当 SQLite 报告数据库损坏时,使用第二个构造函数中的errorHandler参数。

如果我们的数据库版本号不是默认的1,则SQLiteOpenHelper将触发其onUpgrade()方法。SQLiteOpenHelper类的重要方法如下:

  • synchronized void close()
  • synchronized SQLiteDatabase getReadableDatabase()
  • synchronized SQLiteDatabase getWritableDatabase()
  • abstract void onCreate(SQLiteDatabase db)
  • void onOpen(SQLiteDatabase db)
  • abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)

同步close()方法关闭任何打开的数据库对象。synchronized关键字防止线程和内存一致性错误。

接下来的两个方法getReadableDatabase()getWriteableDatabase()是实际创建或打开数据库的方法。两者返回相同的SQLiteDatabase对象;区别在于getReadableDatabase()将返回一个可读的数据库,以防它不能返回一个可写的数据库,而getWriteableDatabase()返回一个可写的数据库对象。如果无法打开数据库进行写入,则getWriteableDatabase()方法将抛出SQLiteException。在getReadableDatabase()的情况下,如果一个数据库无法打开,就会抛出同样的异常。

我们可以在数据库对象上使用SQLiteDatabase类的isReadOnly()方法来了解数据库的状态。对于只读数据库,它返回true

如果数据库还不存在,调用方法将调用onCreate()方法。否则,它将根据版本号调用onOpen()onUpgrade()方法。 onOpen()方法应在更新数据库前检查isReadOnly()方法。数据库一旦打开,就会被缓存以提高性能。最后需要调用close()方法关闭数据库对象。

onCreate()onOpen()onUpgrade()方法是为子类设计的,用于实现预期的行为。第一次创建数据库时调用onCreate()方法。这是我们通过使用 SQLite 语句创建表的地方,我们在前面的示例中看到了这一点。onOpen()方法在数据库配置完成后和数据库模式创建、升级或降级(如有必要)后触发。这里应该借助isReadOnly()方法检查读/写状态。

当数据库需要根据提供给它的版本号进行升级时,调用onUpgrade()方法。默认情况下,数据库版本为1,当我们增加数据库版本号并发布新版本时,将执行升级。

本章的代码包中有一个简单的例子说明了 SQLiteOpenHelper 类的使用;我们会用它来解释:

class SQLiteHelperClass
    {
    ...
    ...
    public static final int VERSION_NUMBER = 1;

    sqlHelper =
       new SQLiteOpenHelper(context, "ContactDatabase", null,
      VERSION_NUMBER)
    {

      @Override
      public void onUpgrade(SQLiteDatabase db,   
            int oldVersion, int newVersion) 
      {

        //drop table on upgrade
        db.execSQL("DROP TABLE IF EXISTS " 
                + TABLE_CONTACTS);
        // Create tables again
        onCreate(db);

      }

   @Override
   public void onCreate(SQLiteDatabase db)
   {
      // creating table during onCreate
      String createContactsTable = 
 "CREATE TABLE "
 + TABLE_CONTACTS + "(" 
 + KEY_ID + " INTEGER PRIMARY KEY," 
 + KEY_NAME + " TEXT,"
 + KEY_NUMBER + " INTEGER" + ")";

        try {
       db.execSQL(createContactsTable);
        } catch(SQLException e) {
          e.printStackTrace();
        }
   }

   @Override
   public synchronized void close()
   {
      super.close();
      Log.d("TAG", "Database closed");
   }

   @Override
   public void onOpen(SQLiteDatabase db)
   {
         super.onOpen(db);
         Log.d("TAG", "Database opened");
   }

};

...
... 

//open the database in read-only mode
SQLiteDatabase db = SQLiteOpenHelper.getWritableDatabase();

...
...

//open the database in read/write mode
SQLiteDatabase db = SQLiteOpenHelper.getWritableDatabase();

类型

下载示例代码

您可以从您在http://www.packtpub.com的账户中下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问http://www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

SQliteDatabaSe 类

现在你已经熟悉了帮助者类,它启动了安卓系统中 SQLite 数据库的使用,是时候看看核心SQLiteDatabase类了。SQLiteDatabase是在 Android 中使用 SQLite 数据库所需的基类,提供了打开、查询、更新和关闭数据库的方法。

SQLiteDatabase类有 50 多种方法可用,每种方法都有自己的细微差别和用例。我们将涵盖最重要的方法子集,并允许您在空闲时探索一些重载的方法,而不是详尽的列表。在任何时候,你都可以在http://developer . Android . com/reference/Android/database/SQLite/sqliteddatabase . html查阅SQLiteDatabase课程的完整在线安卓文档。

SQLiteDatabase类的一些方法如下表所示:

  • public long insert (String table, String nullColumnHack, ContentValues values)
  • public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
  • public Cursor rawQuery(String sql, String[] selectionArgs)
  • public int delete (String table, String whereClause, String[] whereArgs)
  • public int update (String table, ContentValues values, String whereClause, String[] whereArgs)

让我们用一个例子来看看这些SQLiteDatabase类的作用。我们将在表格中插入姓名和号码。然后我们将使用原始查询从表中取回数据。在此之后,我们将通过delete()update()方法,这两种方法都将以id为参数来识别我们打算删除或更新数据库表中的哪一行数据:

public void insertToSimpleDataBase() 
{
   SQLiteDatabase db = sqlHelper.getWritableDatabase();

   ContentValues cv = new ContentValues();
   cv.put(KEY_NAME, "John");
   cv.put(KEY_NUMBER, "0000000000");
   // Inserting values in different columns of the table using
   // Content Values
   db.insert(TABLE_CONTACTS, null, cv);

   cv = new ContentValues();
   cv.put(KEY_NAME, "Tom");
   cv.put(KEY_NUMBER, "5555555");
   // Inserting values in different columns of the table using
   // Content Values
   db.insert(TABLE_CONTACTS, null, cv);
}
...
...

public void getDataFromDatabase()
{  
   int count;
   db = sqlHelper.getReadableDatabase();
   // Use of normal query to fetch data
   Cursor cr = db. query(TABLE_CONTACTS, null, null, 
                           null, null, null, null);

   if(cr != null) {
      count = cr.getCount();
      Log.d("DATABASE", "count is : " + count);
   }

   // Use of raw query to fetch data
   cr = db.rawQuery("select * from " + TABLE_CONTACTS, null);
   if(cr != null) {
      count = cr.getCount();
      Log.d("DATABASE", "count is : " + count);
   }

}
...
...

public void delete(String name)
 {
     String whereClause = KEY_NAME + "=?";
     String[] whereArgs = new String[]{name};
     db = sqlHelper.getWritableDatabase();
     int rowsDeleted = db.delete(TABLE_CONTACTS, whereClause, whereArgs);
 }
...
...

public void update(String name)
 {
     String whereClause = KEY_NAME + "=?";
     String[] whereArgs = new String[]{name};
     ContentValues cv = new ContentValues();
     cv.put(KEY_NAME, "Betty");
     cv.put(KEY_NUMBER, "999000");
     db = sqlHelper.getWritableDatabase();
     int rowsUpdated = db.update(TABLE_CONTACTS, cv, whereClause, whereArgs);
 }

内容值

ContentValues本质上是一组键值对,其中键代表表的列,值是要插入该列的值。因此,在values.put("COL_1", 1);的情况下,列是COL_1,为该列插入的值是1

以下是一个例子:

ContentValues cv = new ContentValues();
cv.put(COL_NAME, "john doe");
cv.put(COL_NUMBER, "12345000");
dataBase.insert(TABLE_CONTACTS, null, cv);

光标

查询恢复一个Cursor对象。一个Cursor对象描述了一个查询的结果,并从根本上指向查询结果的一行。通过这种方法,安卓可以高效地缓冲查询结果;因为它不需要将所有数据加载到内存中。

要获取结果查询的元素,可以使用getCount()方法。

要在单个数据行中导航,可以使用moveToFirst()moveToNext()方法。isAfterLast()方法允许您分析输出的结束是否已经到达。

Cursor对象提供类型化的get*()方法,例如getLong(columnIndex)getString(columnIndex)方法,以获得结果当前位置的列数据的入口。columnIndex是您将要访问的列的编号。

Cursor对象还提供了getColumnIndexOrThrow(String)方法,允许您获取表的列名的列索引。

要关闭Cursor对象,将使用close()方法调用。

数据库查询返回一个游标。该接口提供对结果集的随机读写访问。它指向查询结果的一行,这使得安卓能够有效地缓冲结果,因为现在不需要将所有数据加载到内存中。

返回的光标的指针指向第 0 个位置,即光标的第一个位置。我们需要调用Cursor对象上的moveToFirst()方法;它将光标指针指向第一个位置。现在我们可以访问第一条记录中的数据。

如果来自多个线程,游标实现应该在使用游标时执行自己的同步。需要关闭一个游标,通过调用close()方法释放该对象持有的资源。

我们将遇到的其他一些支持方法如下:

  • getCount()方法:该返回结果查询中的元素数量。
  • get*()方法:这些用于访问结果当前位置的列数据,例如getLong(columnIndex)getString(columnIndex)
  • moveToNext()方法:此将光标移动到下一行。如果光标已经经过结果集中的最后一个条目,它将返回false

总结

我们在本章中介绍了 SQLite 特性及其内部体系结构。我们首先通过观察 SQLite 的显著特性来讨论是什么让它如此受欢迎,然后我们讨论了 SQLite 的底层架构,并复习了语法和数据类型等数据库基础知识,最后讨论了安卓系统中的 SQLite。我们探索了在安卓系统中使用 SQLite 的安卓应用编程接口。

在下一章中,我们将专注于发扬本章所学的知识,并将其应用于构建安卓应用。我们将关注用户界面元素,并将用户界面连接到数据库组件。