zoukankan      html  css  js  c++  java
  • Android学习笔记36:使用SQLite方式存储数据

    在Android中一共提供了5种数据存储方式,分别为:

      (1)Files:通过FileInputStream和FileOutputStream对文件进行操作。具体使用方法可以参阅博文《Android学习笔记34:使用文件存储数据》。

      (2)Shared Preferences:常用来存储键值对形式的数据,对系统配置信息进行保存。具体使用方法可以参阅博文《Android学习笔记35:使用Shared Preferences方式存储数据》。

      (3)Content Providers:数据共享,用于应用程序之间数据的访问。

      (4)SQLite:Android自带的轻量级关系型数据库,支持SQL语言,用来存储大量的数据,并且能够对数据进行使用、更新、维护等操作。

      (5)Network:通过网络来存储和获取数据。

      本篇博文介绍第四种方式,通过Android自带的SQLite数据库存储数据。

    1.SQLite简介

      SQLite是一款开源的、嵌入式关系型数据库,第一个版本Alpha发布于2000年。SQLite在便携性、易用性、紧凑性、高效性和可靠性方面有着突出的表现。

      SQLite和C/S模式的数据库软件不同,它是一款嵌入式数据库,没有独立运行的进程,与所服务的应用程序在应用程序进程空间内共生共存。它的代码与应用程序代码也是在一起的,或者说嵌入其中,作为托管它的程序的一部分。因此不存在数据库的客户端和服务器,使用SQLite一般只需要带上它的一个动态库,就可以享受它的全部功能。

      数据库服务器在程序中的好处是不需要网络配置或管理。将数据库客户端与服务器运行在同一个进程中,可以省去不少的操作及麻烦:不用担心防火墙或者地址解析;不用浪费时间管理复杂的授权和权限;可以减少网络调用相关的消耗;可以简化数据库管理并使程序更容易部署。

      SQLite数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但是只有一个可以写入数据。在某个进程向数据库执行写操作之前,必须获得独占锁定。在发出独占锁定后,其他的读写操作将不会再发生。

      此外,SQLite数据库中的所有信息(比如表、视图、触发器等)都包含在一个文件内,方便管理和维护。SQLite数据库还支持大部分操作系统,除电脑上使用的操作系统之外,很多手机上使用的操作系统同样可以运行。同时,SQLite数据库还提供了多语言的编程接口,供开发者使用。

    2.SQL基本命令

      SQL是与关系型数据库通信的唯一方式。它专注于信息处理,是为构建、读取、写入、排序、过滤、映射、分组、聚集和通常的管理信息而设计的声明式语言。

      在讲解SQL基本命令之前,有必要先了解一下SQLite所支持的数据类型都有哪些。

    2.1 SQLite支持的数据类型

      SQLite采用动态数据存储类型,会根据存入的值自动进行判断。SQLite支持以下5种数据类型:

      (1)NULL:空值

      (2)INTEGER:带符号的整型

      (3)REAL:浮点型

      (4)TEXT:字符串文本

      (5)BLOB:二进制对象

    2.2 SQL基本命令

      表是探索SQLite中SQL的起点,也是关系型数据库中信息的标准单位,所有的操作都是以表为中心的。那么如何使用SQL命令创建一张表呢?

    2.2.1创建表

      表是由行和列组成的,列称为字段,行称为记录。

      使用CREATE命令可以创建表,CREATE命令的一般格式为:

      CREATE [TEMP/TEMPORARY] TABLE table_name (column_definitions [, constraints]);

      其中,[]中的内容是可选的,用TEMP或TEMPORARY关键字声明的表是临时表,这种表只存活于当前会话,一旦连接断开,就会被自动销毁。如果没有明确指出创建的表是临时表,则创建的是基本表,将会在数据库中持久存在,这也是数据库中最常见的表。

      CREATE TABLE命令至少需要一个表名和一个字段名,上述命令中的table_name表示表名,表名必须与其他标识符不同。column_definitions由用逗号分隔的字段列表组成,每个字段定义包括一个名称、一个域(类型)和一个逗号分隔的字段约束。其中,域是指存储在该列的信息的类型,约束用来控制什么样的值可以存储在表中或特定的字段中。

      一条创建表的命令示例如下:

    1   CREATE TABLE tab_student (studentId INTEGER PRIMARY KEY AUTOINCREMENT, 
    2                             studentName VARCHAR(20), 
    3                             studentAge INTEGER);

      如上,我们创建了一个名为tab_student的表,该表包含3个字段:studentId、 studentName和studentAge,其数据类型分别为:INTEGER、VARCHAR和INTEGER。

      此外,通过使用关键字PRIMARY KEY,我们指定了字段studentId所在的列是主键。主键确保了每一行记录在某种方式上与表中的其他行记录是不同的(唯一的),进而确保了表中的所有字段都是可寻址的。

      SQLite为主键提供自增长功能,当定义字段类型为INTEGER PRIMARY KEY时,SQLite将为该字段创建默认值,该默认值确保整数值是唯一的。SQLite使用64-bit单符号整数主键,因此,该字段的最大值是9,223,372,036,854,775,807。当达到最大值时,SQLite会自动搜索该字段还未使用的值,并作为要插入的值。从表中删除记录时,rowid可能被回收并在后面的插入中使用。因此,新创建的rowid不一定是按照严格顺序增长的。如果想要SQLite使用唯一的自动主键值,而不是填补空白,可以在主键定义INTEGER PRIMARY KEY中加入关键字AUTOINCREMENT。AUTOINCREMENT关键字阻止rowid回收,它将为新插入的记录产生新的(不是回收的)rowid。

    2.2.2插入记录

      使用INSERT命令可以一次插入一条记录,INSERT命令的一般格式为:

      INSERT INTO tab_name (column_list) VALUES (value_list);

      其中,tab_name指明将数据插入到哪个表中,column_list是用逗号分隔的字段名称,这些字段必须是表中存在的,value_list是用逗号分隔的值列表,这些值是与column_list中的字段一一对应的。

      比如,向刚才创建的tab_student表中插入一条记录,便可以使用如下的语句完成:


      INSERT INTO tab_student (studentId, studentName, studentAge) VALUES (1, “jack”, 23);

      通过以上的语句,便插入了一条studentName=”jack”, studentAge=”23”的记录,该记录的主键为studentId=1。

    2.2.3更新记录

      使用UPDATE命令可以更新表中的记录,该命令可以修改一个表中一行或者多行中的一个或多个字段。UPDATE命令的一般格式为:

      UPDATE tab_name SET update_list WHERE predicate;

      其中,update_list是一个或多个字段赋值的列表,字段赋值的格式为column_name=value。WHERE子句使用断言识别要修改的行,然后将更新列应用到这些行。

      比如,要更新刚才插入到tab_student表中的记录,便可以使用如下的语句完成:


      UPDATE tab_student SET studentName=”tom”, studentAge=”25” WHERE studentId=1;

      通过以上的语句,便可以将刚才插入的主键为studentId=1的记录更新为studentName=”tom”, studentAge=”25”了。

    2.2.4删除记录

      使用DELETE命令可以删除表中的记录,DELETE命令的一般格式为:

      DELETE FROM table_name WHERE predicate;

      其中,table_name指明所要删除的记录位于哪个表中。和UPDATE命令一样,WHERE子句使用断言识别要删除的行。

      比如,要删除刚才插入的记录,便可以使用如下的语句完成:

      
      DELETE FROM tab_student WHERE studentId=1;

    2.2.5查询记录

      SELECT命令是查询数据库的唯一命令。SELECT命令也是SQL命令中最大、最复杂的命令。

      SELECT命令的通用形式如下:

      SELECT [distinct] heading 

      FROM tables 

      WHERE predicate 

      GROUP BY columns 

      HAVING predicate 

      ORDER BY columns 

      LIMIT count,offset;

      其中,每个关键字(如FROM、WHERE、HAVING等)都是一个单独的子句,每个子句由关键字和跟随的参数构成。GROUP BY和HAVING一起工作可以对GROUP BY进行约束。ORDER BY使记录集在返回之前按一个或多个字段的值进行排序,可以指定排序方式为ASC(默认的升序)或DESC(降序)。此外,还可以使用LIMIT限定结果集的大小和范围,count指定返回记录的最大数量,offset指定偏移的记录数。

      在上述的SELECT命令通用形式中,除了SELECT之外,所有的子句都是可选的。目前最常用的SELECT命令由三个子句组成:SELECT、FROM、WHERE,其基本语法形式如下:

      SELECT heading FROM tables WHERE predicate;

      比如,要查询刚才插入的记录,便可以使用如下的语句完成:

      
      SELECT studentId, studentName, studentAge FROM tab_student WHERE studentId=1;

      至此,我们介绍了SQL中最基本和最常用的CREATE、INSERT、UPDATE、DELETE和SELECT命令。当然了,这里只是对其进行了简单的介绍,有关SQLite中SQL命令的详细使用方法,可以参阅《SQLite权威指南》一书的第三章和第四章。

    3.数据库操作辅助类SQLiteOpenHelper

      Android提供了一个重要的类SQLiteOpenHelper,用于辅助用户对SQLite数据库进行操作。

      SQLiteOpenHelper的构造函数原型如下:

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

      其中,参数context表示应用程序运行的环境,包含应用程序所需的共享资源。参数name表示Android的数据库名字。参数factory是SQLiteDatabase.CursorFactory类对象,用于存储查询Android SQLite数据库的结果集。参数version表示应用程序所用的数据库的版本,该版本并非SQLite的真正版本,而是指定应用程序中的SQLite数据库的版本,当该版本号发生变化时,将会触发SQLiteOpenHelper类中的onUpgrade()或onDowngrade()方法。

      SQLiteOpenHelper类的所有方法如图1所示。

    图1 SQLiteOpenHelper类的方法

      其中,close()方法用于关闭SQLiteOpenHelper对象中的SQLite数据库;getReadableDatabase()方法和getWriteableDatabase()方法类似,getReadableDatabase()方法以只读状态打开SQLiteOpenHelper对象中指定的SQLite数据库,任何想要修改数据库的操作都是不允许的;getWriteableDatabase()方法也是打开数据库,但是允许数据库正常的读/写操作;在一个不存在的数据库上调用任何方法时,都会隐式的调用SQLiteOpenHelper对象的onCreate()方法;当应用程序第一次访问数据库时,则会调用onOpen()方法,但是,如果版本号发生了变化的话,则会调用onUpgrade()或onDowngrade()方法。

    4.数据库类SQLiteDatabase

      SQLiteDatabase类用来完成对数据库的操作任务,比如表的选择、插入、更新和删除语句等。

      SQLiteDatabase类中常用的用于执行SQL语句的方法有以下一些。

      (1)execSQL()方法:

      public void execSQL (String sql);

      public void execSQL (String sql, Object[] bindArgs);

      (2)query()方法:

      public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String  groupBy, String  having,String orderBy, String limit);

      public Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal);

      public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String  having,String orderBy);

      public Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);

      (3)queryWithFactory()方法:

      public Cursor queryWithFactory (SQLiteDatabase.CursorFactory cursorFactory, boolean distinct, String table, String[]columns, String selection, String[] selectionArgs, String groupBy, String having, String  orderBy, String  limit,CancellationSignal cancellationSignal);

      public Cursor queryWithFactory (SQLiteDatabase.CursorFactory cursorFactory, boolean distinct, String table, String[]columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);

      (4)rawQuery()方法:

      public Cursor rawQuery (String sql, String[] selectionArgs, CancellationSignal cancellationSignal);

      public Cursor rawQuery (String sql, String[] selectionArgs);

      (5)rawQueryWithFactory()方法:

      public Cursor rawQueryWithFactory (SQLiteDatabase.CursorFactory cursorFactory, String sql, String[]  selectionArgs,String editTable);

      public Cursor rawQueryWithFactory (SQLiteDatabase.CursorFactory cursorFactory, String sql, String[]  selectionArgs,String editTable, CancellationSignal cancellationSignal);

      其中,execSQL()方法都有一个参数sql,这个参数是一个SQL语句。第二个参数bindArgs接收一个数组,数组中的每个成员捆绑了一个查询。execSQL()方法用于运行那些没有返回值的查询语句,比如创建、插入、更新和修改表。

      query()方法和queryWithFactory()方法是在数据库中运行一些轻量级的单查询语句,参数包括table、columns、groupBy、having、orderBy、limit等SQL语句关键字。这些方法允许将SQL语句传递给相关方法,而不必直接使用SQL语句。

      rawQuery()方法和rawQueryWithFactory()方法也都有一个参数sql,用于执行SQL查询语句,返回值是Cursor对象。这两个方法都有一个版本能够接收一个字符串数组selectionArgs作为参数,通过这个参数,SQLiteDatabase对象将把捆绑的SQL语句中的问号(?)用这个数组中的值代替,并按照一一对应的位置关系进行取代。

      SQLiteDatabase类提供了大约50个方法,除此之外还有一些用于打开数据库的方法(如openDatabase()、openOrCreateDatabase()等),用于管理SQLite事务的方法(如beginTransaction()、endTransaction()等),用于测试数据库是否被锁住的方法(如isDbLockedByCurrentThread()、isDbLockedByOtherThread()等),以及获取数据库基本信息的方法(如getMaximumSiza()、getVersion()等)。这里就不一一介绍了,具体可以参阅SQLiteDatabase类的API帮助文档

    5.游标类Cursor

      在Android中,查询数据是通过Cursor类来实现的,当我们使用SQLiteDatabase.query()或SQLiteDatabase.rawQuery()方法时,会得到一个Cursor对象,Cursor指向的就是每一条记录,它提供了很多有关查询的方法,如图2所示。

    图2 Cursor类的常用方法

    6.封装接口

      有了以上的基础,我们便可以按照MVC的架构,封装一个接口层,在该接口层中实现对SQLite数据库的具体操作。

      以下分别以添加数据、更新数据、查询数据为例讲解其具体的实现方法。在实现这些方法之前,我们首先需要创建一张表。这里我创建了一个名为MySQLiteOpenHelper的类,让它继承自SQLiteOpenHelper类,并实现了SQLiteOpenHelper类的onCreate()方法,在该方法里实现创建一张表的操作,具体源代码如下:

    复制代码
    1     /*
    2      * Function  :    创建表
    3      * Author    :    博客园-依旧淡然
    4      */
    5     public void onCreate(SQLiteDatabase db) {
    6         db.execSQL("CREATE TABLE tab_student (studentId INTEGER PRIMARY KEY AUTOINCREMENT, " + 
    "studentName VARCHER(20), " +
    "studentAge INTEGER)"); 7 }
    复制代码

      通过以上的代码,我们创建了一张名为“tab_student”的表,并在该表中创建了三个字段,分别为:studentId、studentName和studentAge。并且指定了studentId字段作为该表的主键。

    6.1添加数据

      添加数据可以使用SQLiteDatabase.execSQL(String sql, Object[] bindArgs)方法来实现,具体如下:

    复制代码
    1     /*
    2      * Function  :    添加数据
    3      * Author    :    博客园-依旧淡然
    4      */
    5     public void addStudentInfo(Student student) {
    6         db = mySQLiteOpenHelper.getWritableDatabase();
    7         db.execSQL("INSERT INTO tab_student (studentId, studentName, studentAge) values (?, ?, ?)", 
    8           new Object[] {student.getStudentId(), student.getStudentName(), student.getStudentAge()});
    9     }
    复制代码

      其中,通过第二个参数bindArgs,使SQL语句中的问号(?)与这个数组中的值形成一一对应关系,从而将值写入到“tab_student”表中的对应字段中。

    6.2更新数据

      更新数据的方法与添加数据的方法大致相同,具体如下:

    复制代码
    1    /*
    2     * Function  :    更新数据
    3     * Author    :    博客园-依旧淡然
    4     */
    5    public void updateStudentInfo(Student student) {
    6        db = mySQLiteOpenHelper.getWritableDatabase();
    7        db.execSQL("UPDATE tab_student SET studentName = ?, studentAge = ? WHERE studentId = ?", 
              new Object[] {student.getStudentName(), student.getStudentAge(), student.getStudentId()});
    8    }
    复制代码

    6.3查询数据

      查询数据时,因为需要返回查询的结果,所以需要使用SQLiteDatabase.rawQuery()方法将查询的结果返回,具体如下:

    复制代码
     1     /*
     2      * Function  :    查询数据
     3      * Author    :    博客园-依旧淡然
     4      */
     5     public Student findStudentInfo(int id) {
     6         db = mySQLiteOpenHelper.getWritableDatabase();
     7         String sql = "SELECT studentId, studentName, studentAge FROM tab_student WHERE studentId = ?";
     8         Cursor cursor = db.rawQuery(sql, new String[] {String.valueOf(id)});
     9         if(cursor.moveToNext()) {
    10             return new Student(cursor.getInt(cursor.getColumnIndex("studentId")),     
    cursor.getString(cursor.getColumnIndex("studentName")), 11 cursor.getInt(cursor.getColumnIndex("studentAge"))); 12 } 13 return null; 14 }
    复制代码

      可以看出,通过使用SQLiteDatabase.rawQuery()方法可以将查询到的结果存入Cursor对象中。然后,我们可以使用Cursor对象的getXXX()方法将查询结果从Cursor对象中取出来。

      当然了,我们还可以根据实际的需要,去实现更多的接口方法,比如,删除数据、获取数据列表、获取数据个数等等。

      封装好了以上的这些接口方法,便可以很方便的在程序中直接调用这些方法,不必再去关心底层数据库的调用,而将精力放在UI界面的设计实现上。

  • 相关阅读:
    PDF文件中的Form保存问题
    Understanding IP Fragmentation
    tcp ip guide IPsec IKE
    Windows安全事件日志中的事件编号与描述
    Cisco PIX fix up and Juniper firewall FTP ALG
    很好的IPSec介绍,详细解释了IKE协商的2个阶段的作用
    virtualbox 下运行Ubuntu 8.10的分辨率和guest additions的问题。
    Fixing the ‘Do you want to display nonsecure items’ message
    windows xp 开始菜单里面所有项目右键不起作用。
    HP backup and recovery manager
  • 原文地址:https://www.cnblogs.com/printN/p/6592479.html
Copyright © 2011-2022 走看看