android之SQLite数据库应用(二)

今天让我们总结下SQLite数据库在android系统中的应用。首先来看一些数据库的介绍:

      SQLite数据库是一种无类型的数据库,这就代表你可以保存任何类型的数据到任何表中的任何列哦,无论这个表在create的时候该列被声明成什么类型。因为SQLite在执行数据库建表语句的时候,会自动的将类型忽略的哦。

     它是一种用C语言编写的嵌入式数据库,它是一个轻量级的数据库,在一些基础简单的语句处理上要比oracle/mysql快很多,而且其对内存的要求很低。

     注意:这里一定要注意,SQLite数据库在一种情况下是要求类型匹配的,当我们建表是如create table table1(id integer primary key),sqlite对应一位integer primary key的列值允许你存储64位的整数。对于SQLite来说对字段不指定类型是完全有效的。Create Table ex1(a, b, c);  -------------------------------------------------------------------------------

  诚然SQLite允许忽略数据类型, 但是仍然建议在你的Create Table语句中指定数据类型. 因为数据类型对于你和其他的程序员交流,或者你准备换掉你的数据库引擎. SQLite支持常见的数据类型, 如:

  CREATE TABLE ex2(

  a VARCHAR(10),

  b NVARCHAR(15),

  c TEXT,

  d INTEGER,

  e FLOAT,

  f BOOLEAN,

  g CLOB,

  h BLOB,

  i TIMESTAMP,

  j NUMERIC(10,5)

  k VARYING CHARACTER (24),

  l NATIONAL VARYING CHARACTER(16)

  );

上面我们对sqlite数据库的基本信息进行了介绍,现在我们一起看看sqlite的数据库语法。

至于像增删改查的语法了,实在是没什么可说的,和我们平时用的sql基本上一摸一样。我这里主要提示下怎么通过sqlite进行分页,要进行分页,需要通过如下语句

select * from tb_name limit 10 offset 1

这里的limit 10代表要获取的数据的个数,offset 1表示从第几行数据开始获取。(第一行的offset为0)

 

---------------------------------------------------------------------

android与sqlite的结合:

 

       android为我们提供了一系列的api对sqlite数据库进行访问。这些类或者接口一般都存储在android.database和android.database.sqlite两个包中。

      如何创建和打开sqlite数据库

      在android中我们通过SQLiteDatabase这个类来操作数据库,获取其对象的方法一般有以下几种:

     1、在SQLiteDatabase中,我们可以看到这些方法

 

参数中的path代表着数据库的路径(如果是在默认路径/data/data/<package_name>/databases/下,则这里只需要提供数据库名称)、factory代表着在创建Cursor对象时,使用的工厂类,如果为null的话,则使用默认的工厂(这里我们可以实现自己的工厂进行某些数据处理哦)、flags代表的是创建表时的一些权限设置,多个权限之间用|分隔。

       open_readonly 代表的是以只读方式打开数据库

       open_readWrite代表以读写方式打开数据库

       create_if_necessary当数据库不存在时创建数据库

       no_localized_collators打开数据库时,不根据本地化语言对数据库进行排序

此外该类还有一个方法,为我们提供了一个内存表(即临时表,当跟数据库的连接被关闭的时候,会被删除)。当创建失败时,返回null

 public static SQLiteDatabase create(CursorFactory factory)

2、和创建、打开文件相似的是,这里context中也为我们提供了一些更加方便快捷的获取数据库对象SQLiteDatabase的方法。

* @mode #MODE_PRIVATE
     * @mode #MODE_WORLD_READABLE
     * @mode #MODE_WORLD_WRITEABLE
     * @mode #deleteDatabase
     *创建或打开一个指定名称的数据库
     */
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory);

此外,还提供了

 public abstract boolean deleteDatabase(String name);

该方法可以删除私有数据库当前目录下的名称为name的数据库。

 public String[] databaseList()则返回与当前activity相关联的所有的私有数据库的名称。

3、第三种办法就是通过实现自己的SQLiteOpenHelper。

SQLiteOpenHelper是系统提供的一个管理数据库表创建和更新的抽象类,我们必须通过继承SQLiteOpenHelper来实现自己的帮助类。

一般我们要重写三个方法,构造器、onCreate方法、onupgrade方法。

SQLiteOpenHelper创建数据库实际上是使用的context里面的方法创建、打开私有目录下的数据库文件。该类给我们提供了两个方法来获取SQLiteDatabase对象。

这里要特别注意,getReadableDatabase()和getWritableDatabase()方法并非和我们想象中的那样,一个返回只读,一个返回可读写的database。

        getWritableDatabase方法,返回一个可读写的database(如果数据库不存在则创建一个),一旦创建成功了,那么这个database数据库就会被缓存,下次再调用该方法时,会直接把缓存中的数据库对象返回给你。当由于磁盘以及写满或者权限问题导致无法获得可写的对象时,会抛出异常!如果过了一段时间,问题已经被修复了,当你再次调用该方法的时候,依旧可以获得一个可读写的database。

       getReadableDatabase方法,正常情况下,返回的结果和getWritableDatabase方法的一模一样。当遇见磁盘写满这种事故是,它不会跑出异常,而是返回一个只读的数据库对象。当我们再以后再调用改方法的时候,如果事故已经被解决掉,那么它会继续返回和getWritableDatabase一样的数据库对象。

------------------------------------------------------------------------------------

利用数据库对象SQLiteDatabase对数据库进行增删改查操作。

    这里要强调一点,无论我们怎么获得的数据库连接,我们可以看到,我们最后都是为了获得一个SQLiteDatabase对象,而我们的所以增删改查方法便都是通过这个对象来实现的。

增加数据:

 参数介绍:

table 要插入数据的表的名称

values:一个ContentValues对象,类似一个map.通过键值对的形式存储值。

conflictAlgon:冲突解决方案。例如当数据表主键的唯一性检测出错的时候,就会按照该值设定的值进行处理。

nullColumnHack:当values参数为空或者里面没有内容的时候,我们insert是会失败的(底层数据库不允许插入一个空行),为了防止这种情况,我们要在这里指定一个列名,到时候如果发现将要插入的行为空行时,就会将你指定的这个列名的值设为null,然后再向数据库中插入。

(这里很多人会迷惑,nullColumnHack到底干什么用的,为什么会出现呢。当我们不设定一列的时候,不都是数据库给设为默认值吗?很多字段设置默认值也是null,这里显示的设置也是null,有什么区别吗,怎么会显示设置了之后就允许插入了呢?笔者为了找到原因,我去查看了源代码)

其实在底层,各种insert方法最后都回去调用insertWithOnConflict方法,这里我们粘贴出该方法的部分实现

 /**
     * General method for inserting a row into the database.
     *
     * @param table the table to insert the row into
     * @param nullColumnHack SQL doesn‘t allow inserting a completely empty row,
     *            so if initialValues is empty this column will explicitly be
     *            assigned a NULL value
     * @param initialValues this map contains the initial column values for the
     *            row. The keys should be the column names and the values the
     *            column values
     * @param conflictAlgorithm for insert conflict resolver
     * @return the row ID of the newly inserted row
     * OR the primary key of the existing row if the input param ‘conflictAlgorithm‘ =
     * {@link #CONFLICT_IGNORE}
     * OR -1 if any error
     */
    public long insertWithOnConflict(String table, String nullColumnHack,
            ContentValues initialValues, int conflictAlgorithm) {
        if (!isOpen()) {
            throw new IllegalStateException("database not open");
        }

        // Measurements show most sql lengths <= 152
        StringBuilder sql = new StringBuilder(152);
        sql.append("INSERT");
        sql.append(CONFLICT_VALUES[conflictAlgorithm]);
        sql.append(" INTO ");
        sql.append(table);
        // Measurements show most values lengths < 40
        StringBuilder values = new StringBuilder(40);

        Set<Map.Entry<String, Object>> entrySet = null;
        if (initialValues != null && initialValues.size() > 0) {
            entrySet = initialValues.valueSet();
            Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
            sql.append(‘(‘);

            boolean needSeparator = false;
            while (entriesIter.hasNext()) {
                if (needSeparator) {
                    sql.append(", ");
                    values.append(", ");
                }
                needSeparator = true;
                Map.Entry<String, Object> entry = entriesIter.next();
                sql.append(entry.getKey());
                values.append(‘?‘);
            }

            sql.append(‘)‘);
        } else {
            sql.append("(" + nullColumnHack + ") ");
            values.append("NULL");
        }

这里我们可以看到,当我们的ContentValues类型的数据initialValues为null,或者size<=0时,就会再sql语句中添加nullColumnHack的设置。我们可以想象一下,如果我们不添加nullColumnHack的话,那么我们的sql语句最终的结果将会类似insert into tableName()values();这显然是不允许的。而如果我们添加上nullColumnHack呢,sql将会变成这样,insert into tableName (nullColumnHack)values(null);这样很显然就是可以的。

删除相关:

 

 

table:要删除的数据所在的表的名称

whereClause:条件语句,注意这里的条件语句并不包括where这个单词。例如 name="chenzheng_java" and age=23

whereArgs:我们再where子句中,为了防止sql注入,我们通常会通过?的方式进行代替,然后再为?赋值。

当删除正常执行时,返回的是删除的数据的个数;否则的话,返回0.如果我们想全部删除的话,whereClause设置为“1”即可。

查询相关:

     主要方法有

参数介绍:

table 表名称

columns 要查询的列名,以数组的形式提供。

selection:条件语句

selectionArgs:条件语句中?的内容参数、

limit:所取的记录个数限制

eidtTable :第一个可编辑的表名称

其中rawQuery是通过sql语句进行查询的哦。

修改操作:

 

----------------------------------------------------------------------------

事务相关:

 和事务相关的方法

transactionListener:当事务执行了begin/commit/rollback方法时触发

关于Exclusive和immediate模式的说明:

Exclusive代表排他的、独立的,当我们用这种模式获取事务时,在我们的事务没有结束之前,其他的线程和进程是既不能读取该数据库,也不能对数据库进行任何写操作;

immediate代表即时的,当我们用这种模式获取事务的时候,其他进程和线程无法写入该数据库,但是却可以正常的读取。

设置事务提交的方法

setTransactionSuccessful() ,记得一定要调用此方法,要不事务不会提交,而是会自动回滚的。

结束事务:endTransaction();一般在调用了setTransactionSuccessful()方法之后接着调用endTransaction()方法、

--------------------------------------------------------------------------------------

记得,在程序的最后,一定要调用SQLiteDatabase的close()方法关闭数据库。

---------------------------------------------------------------------------------------

结果集的遍历:

android.database.Cursor接口

 

 

如果你用过JDBC中的游标,那你对这里面的所有方法应该都不陌生。这里就不多详细介绍用法了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。