Android 应用开发】Android 数据存储 之 SQLite数据库详解(一)

SQLiteDataBase示例程序下载地址 :


-- GirHub - https://github.com/han1202012/SQLite_NewsList.git.



SQLiteOpenHelper示例程序下载地址 :


-- GitHub : https://github.com/han1202012/NewsSearch.git .




SQLite数据库简介 :


-- 轻量级 : SQLite数据库是一个轻量级的数据库, 适用于少量数据的CURD;


-- 文件本质 : SQLite数据库支持大部分SQL语法, 允许使用SQL语句操作数据库, 其本质是一个文件, 不需要安装启动;


-- 数据读写 : SQLite数据库打开只是打开了一个文件的读写流, 如果有大数据量读写, 需要高并发存储, 那么就不应该使用SQLite;






一. 使用SQLiteDataBase操作数据库



SQLiteDataBase简介 : SQLIteDataBase代表一个数据库, 其本质是一个数据库文件, 创建该类对象, 可以使用该对象的一系列方法操作数据库;




1. SQLiteDataBase方法介绍



(1) 打开数据库方法



使用静态方法打开文件对应的数据库, 数据库文件通常是 文件名.db 形式的;




1> 根据标志位创建数据库



打开数据库: 根据标志位flag要求打开数据库, 这个方法很强大;



public static SQLiteDatabase openDatabase (String path, SQLiteDatabase.CursorFactory factory, int flags)

参数介绍 :

-- 参数① path : path 要打开 或者 需要创建的 数据库文件的路径;


-- 参数② factory : 当打开的数据库执行查询语句的时候 会创建一个Cursor对象, 这时会调用Cursor工厂类 factory, 可以填写null默认值;


-- 参数③ flag :


OPEN_READWRITE 打开一个读写数据库, 如果磁盘满了, 之前写入的也作废;


READ_OPENONLY 打开只读数据库, 这时读取数据库的可靠方法;


CREATE_IF_NECESSARY 打开数据库, 如果数据库不存在, 就创建这个数据库;


NO_LOCALIZED_CALLATORS 打开数据库 不根据本地语言顺序进行排序, 使用这种模式创建数据库, 排序器不会被创建, 使用这个数据库 和 创建这个数据库的时候必须都使用这个标识, 如果这个标识被使用了, 那么setLocal()方法将不会起到任何作用;




2> 根据文件打开或创建数据库



打开数据库 : 根据数据库文件 对象打开数据库, 如果数据库不存在就创建数据库;



public static SQLiteDatabase openOrCreateDatabase (File file, SQLiteDatabase.CursorFactory factory)

相当于   openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY);



3> 根据路径打开或创建数据库



打开数据库 : 根据数据库文件 路径打开数据库, 如果数据库不存在就创建数据库;



public static SQLiteDatabase openOrCreateDatabase (String path, SQLiteDatabase.CursorFactory factory)

相当于   openDatabase(path, factory, CREATE_IF_NECESSARY);




(2) 操作数据库方法



1> 执行SQL语句动态参数



执行SQL语句, 如果需要动态传入SQL语句参数, 将动态参数放入一个Object[]数组中;



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

注意 : 该方法 可以 进行增删改操作,  不能进行查询 操作;

该方法适用于 :修改表结构,创建和删除表 触发器 视图 索引等,重建数据库表的索引,数据库升级,事物中保存点,没有返回值的语句;


参数介绍 :


-- 参数① sql: 要执行的SQL语句, 只能执行一条, 多条语句用分号隔开不管用, 参数使用 "?" 占位符代替;


-- 参数② bingArgs: 替换上面SQL语句中的 "?" 占位符, 按照数组中的顺序依次替换;




2> 执行固定的SQL语句



该方法执行固定的SQL语句, 没有参数, 用法与上面的 execSQL(sql, bindArgs)一致;


public void execSQL (String sql)



3> 插入数据



插入数据 : 向数据库中的 一个表 插入 一行 数据;



public long insert (String table, String nullColumnHack, ContentValues values)

参数介绍 :

-- 参数① table : 数据库中的表名, 要插入数据的表;


-- 参数② nullColumnHack : 该参数是可选的, 数据库表中不允许插入一行空的数据, 插入数据至少有一列不为null才能插入, 如果后面的values是null, 并且不知道列的名称, 那么插入操作会失败, 为了避免这种情况, 就出现了本参数, 为了防止 values为null的情况;


-- 参数③ values : 相当于一个Map集合,键 是列名,值 是对应列名要插入的数据;


插入原则 : 不管 第三个 ContentValues参数 是否为null, 执行insert()方法都会添加一条记录, 如果values参数为null, 会添加一个除主键之外其它字段都为null的记录;


nullColumnHack参数作用分析SQL语句 : 在SQL语句中在表名后面必须跟着一个列名, 例如 " insert into appale_info(name) values("乔帮主") ", 这是values参数不为null的情况下,如果values参数为null, 那么导致表名 "apple_info" 后面的列名也为null, 这样SQL语句就不合法了, 因此这里必须加上一个默认的列名, 以防values参数为null;




实例 :



 /

/创建表数据, 键 为 列名, 值 为 对应的表数据
  ContentValues values = new ContentValues();
  values.put("name", "乔帮主");
  values.put("age", 54);
  //向表中插入数据, 如果values为null, 就会添加一个
  long rowsNum = db.insert("apple_info", "name", values);



上面代码转成SQL语句 :


insert into apple_info (name, age) values ('乔帮主', 54)


假如values为null转化成SQL语句 :


insert into apple_info (name) values ()





4> 更新数据



更新指定表中的特定数据 :



public int update (String table, ContentValues values, String whereClause, String[] whereArgs)

参数介绍 :


-- 参数① table : 数据库表名称;


-- 参数② values : 该值类似Map集合, 键 是 列名, 值 是 要更新的数据, 这个值可以为null, 如果为null 这些数据会被清空;


-- 参数③ whereClause: where选择语句, 选择那些行进行数据的更新, 如果该参数为 null, 就会修改所有行;


-- 参数④ whereArgs : where选择语句的参数, 逐个替换 whereClause 中的占位符;


返回值 : 返回修改的行数;


实例 :



//创建表数据, 键 为 列名, 值 为 对应的表数据
  ContentValues values = new ContentValues();
  values.put("name", "乔帮主");
  values.put("age", 54);
  int result = db.update("apple_info", values, "name=?", new String[]{"乔帮主"});
转化为SQL语句: 
update apple_info set name='乔帮主', age=56 where name='乔帮主'



5> 删除数据



删除指定表中特定数据 :



public int delete (String table, String whereClause, String[] whereArgs)

参数介绍 :

-- 参数① table : 要操作的数据库表名;


-- 参数② whereClause : where选择语句, 选择哪些行要被删除, 如果为null, 就删除所有行;


-- 参数③ whereArgs : where语句的参数, 逐个替换where语句中的 "?" 占位符;


实例 :



int result = db.delete("apple_info", "name=?", new String[]{"乔帮主"});
转化为SQL : 
delete apple_info where name='乔帮主'



6> 最全面查询



.



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

参数介绍 :


-- 参数① distinct : 是否去重复, true 去重复;


-- 参数② table : 要查询的表名;


-- 参数③ columns : 要查询的列名, 如果为null, 就会查询所有的列;


-- 参数④ whereClause : 条件查询子句, 在这里可以使用占位符 "?";


-- 参数⑤ whereArgs : whereClause查询子句中的传入的参数值, 逐个替换 "?" 占位符;


-- 参数⑥ groupBy: 控制分组, 如果为null 将不会分组;


-- 参数⑦ having : 对分组进行过滤;


-- 参数⑧ orderBy : 对记录进行排序;


-- 参数⑨ limite : 用于分页, 如果为null, 就认为不进行分页查询;


-- 参数⑩ cancellationSignal : 进程中取消操作的信号, 如果操作被取消, 当查询命令执行时会抛出 OperationCanceledException 异常;


实例 :



 

Cursor cursor = db.query(true, "apple_info", new String[]{"_id, name, age"},
    "name like ?", new String[]{"乔%"}, 
    null, null, 
    "_id desc", "5, 10");
  cursor.close();



7> 执行SQL查询语句



通过执行SQL语句, 查询结果;



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

参数解析 :

-- 参数① sql : 要执行的SQL语句, 可以使用 "?" 作为占位符;


-- 参数② selectionArgs : sql语句中的参数, 按照次序依次替换占位符 "?";




(3) SQLite中的事务



开启事务 :



public void beginTransaction ()



结束事务 :


public void endTransaction ()



判断当前是否处于事务中 : 如果处于返回true, 反之返回false;


public boolean inTransaction ()



提交事务 : 程序执行endTransaction()方法的时候提交事务 还是 回滚事务, 取决于是否调用了 setTransactionSuccessful()方法, 如果事务执行中调用了改方法, 则提交事务, 如果没有执行该方法, 就会回滚事务;



示例代码 :






2. Cursor的相关方法



(1) 移动记录指针方法



1> 移动到指定行数



方法作用 : 将记录指针向上 或者 向下移动offset行, offset为正数就是向下,为负数 就是向上;



public abstract boolean move (int offset)

参数介绍 : offset是移动的相对行数;



2> 移动到第一行



记录指针移动到第一行, 如果移动成功返回true;



public abstract boolean moveToFirst ()




3> 移动到最后一行

记录指针移动到最后一行, 如果移动成功返回true;



public abstract boolean moveToLast ()





4> 移动到上一行



移动到上一行, 成功返回true;



public abstract boolean moveToPrevious ()




5> 移动到下一行



移动到下一行, 成功返回true;



public abstract boolean moveToNext ()




6> 移动到指定行



移动到指定行, 成功返回true;



public abstract boolean moveToPosition (int position)


(2) 获取记录数据方法



获取某个类型的数据 :



public abstract float getFloat (int columnIndex);//获取浮点型数据

public abstract int getInt (int columnIndex);//获取整型数据

public abstract long getLong (int columnIndex);//获取长整型数据

public abstract short getShort (int columnIndex);//获取短整型数据

public abstract String getString (int columnIndex);//获取字符串数据

参数介绍 : 参数是列的标号, 注意该标号从 0 开始计数;




3. sqlite3工具介绍



工具简介 : sqlite3 是一个简单的数据库管理工具, 该用于位于 SDK tools 目录下;




获取数据库文件 : 使用虚拟机运行程序在 data/data/包名 安装目录下, 数据库文件在里面可以找到;




打开数据库 : 进入cmd命令行, 使用sqlite3 数据库文件 命令打开数据库;




常用的sqlite3 工具命令:


-- 打开数据库: sqlite3 文件路径名 ;


-- 查看当前数据库 : .database ;


-- 查看当前数据库中的表 : .tables ;


-- 查看sqlite3的帮助 : .help ;




4. SQLite的数据存储格式



支持的数据类型 : SQLite数据库 内部 只支持null,integer,real(浮点型),text(文本),blob(二进制数据) 五种数据类型;


数据类型转换 : SQLite可以接受varchar(n), char(n), decimal(p,s) 等数据类型, 不过内部的机制使将这个数据类型转换成上面的五种数据类型进行存储;


无类型限制: SQLite允许将各种类型的数据保存到各种类型的字段中, 没有严格的某个字段 必须存放某个类型的数据这样的限制, 因此创建数据库 和 插入数据的时候不用关心这个列的数据类型;


-- eg: 在SQLite中可以将字符串数据放到整型字段中, 但是主键id, 不能随便放其它数据, 只能存放64卫整数;






二. 新闻列表程序实例



1. 要点解析



(1) 数据库相关操作



使用 openOrCreateDatabase()方法创建数据库 : 传入数据库的路径 和 CursorFactory对象;


-- Context.getFilesDir()方法: 该方法返回 内存中应用安装目录中的 文件存储目录的绝对路径, 在这里是 "data/data/shuliang.han.database/files", 整个数据库的完整路径是 :"data/data/shuliang.han.database/files/news.db";



//打开或者创建数据库, 这里是创建数据库
  db = SQLiteDatabase.openOrCreateDatabase(this.getFilesDir().toString() + "/news.db", null);


创建表 :  使用 execSQL()方法 执行SQL语句创建表;

db.execSQL("create table news_table (" +
        "_id integer primary key autoincrement, " +
        "news_tittle varchar(50), " +
        "news_content varchar(5000))");


插入数据 : 调用 execSQL()方法执行插入操作, 传入一个带参数的SQL语句;


db.execSQL("insert into news_table values(null, ?, ?)", new String[]{tittle, content});


查询数据 : 使用 rawQuery()方法, 传入 SQL语句 和 CursorFactory对象, 返回一个Cursor对象;


Cursor cursor = db.rawQuery("select * from news_table", null);


关闭数据库 : 检查 SQLiteDataBase对象 是否为null, 并且 是打开的, 如果符合上面的两个条件, 将db关闭, 这个方法一般是在onDestroy()方法中进行;

@Override
    protected void onDestroy() {
  super.onDestroy();
  //在Activity销毁的时候, 如果没有
  if(db != null && db.isOpen())
    db.close();
    }



(2) ListView相关操作



创建SimpleCursorAdapter适配器 :


参数解析 :


-- 参数① context : 上下文对象;


-- 参数② resource : ListView条目的布局;


-- 参数③ cursor : 从数据库表中查询出来的记录;


-- 参数④ string[]: 数据库中表的字段名称;


-- 参数⑤ int[]: 将数据库中每行的字段 按照对应顺序 放入到该数组对应组件中;



 

SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(
    getApplicationContext(), 
    R.layout.item, 
    cursor, 
    new String[]{"news_tittle", "news_content"}, 
    new int[]{R.id.tittle, R.id.content});



将创建的适配器设置给ListView : 执行这个方法会刷新ListView的显示;


listView.setAdapter(cursorAdapter);


上一篇:【Android 系统开发】Android JNI 之 JNIEnv 解析(一)


下一篇:【Android 应用开发】Android - 时间 日期相关组件