第五章 SQLite数据库

  • SQLite数据库的基本操作
  • 使用sqlite3工具操作数据库
  • 使用ListView控件展示数据

一、SQLite数据库简介

SQLite是一个轻量级数据库,占用资源少,遵守ACID事务属性,同时还支持SQL语言、事务处理等功能。

SQLite没有服务器进程,它通过文件保存数据,该文件是跨平台的,可以放在其他平台中使用。

在保存数据时,支持NULL、Integer、Real(浮点型数字)、Text(字符串文本)和Blob(二进制对象)5种数据类型。但实际上SQLite也接收varchar(n)、char(n)、decimal(p, s)等数据类型,只不过在运算或保存时会转换成对应的5种数据类型。因此,可以将各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型。这也是SQLite数据库的最大特点。

二、SQLite数据库的使用

1. SQLite操作API

为了方便使用SQLite数据库,Android SDK提供了一系列对数据库进行操作的类和接口。

(1) SQLiteOpenHelper类

SQLiteOpenHelper是一个抽象类,该类用于创建数据库和数据库版本更新。该类常用方法如图5-1所示


图 5-1

(2) SQLiteDatabase类

SQLiteDatabase是一个数据库访问类,该类封装了一系列数据库操作的API,可以对数据库进行增、删、改、查操作。该类的常用方法如图5-2所示。


图 5-2

(3) Cursor接口

Cursor是一个游标接口,在数据库操作中作为返回值,相当于结果集ResultSet。该类的常用方法如图5-3所示。


图 5-3

2. SQLite常用操作

(1) 创建SQLite数据库

Android系统推荐使用SQLiteOpenHelper的子类创建SQLite数据库,因此需要创建一个类继承自SQLiteOpenHelper,重写onCreate()方法,并在该方法中执行创建数据库的命令。

示例代码

public class PersonSQLiteOpenHelper extends SQLiteOpenHelper{
    // 构造方法,用来定义数据库的名称,数据库查询的结果集,数据库的版本
    public PersonSQLiteOpenHelper(Context context){
        super(context, "person.db", null, 5);
    }

    // 数据库第一次被创建时调用该方法
    public void onCreate(){
        // 初始化数据库的表结构,执行一条建表的SQL语句
        db.execSQL("create table person (id integer primary key autoincrement, name varchar(20), number varchar(20))");
    }

    // 当数据库的版本号增加时调用,如果数据库的版本号不增加,该方法则不会调用
    public void onUpgrade(){
        db.execSQL("alter table person add account varchar(20)");
    }
}

值得注意的是,创建的数据库被放置在/data/data/packagename/databases目录下。

(2) 增

示例代码:使用SQLiteDatabase对象向person表中插入一条数据

public void add(String name, String number){
    // 获取一个可读写的SQLiteDatabase对象
    SQLiteDatabase db=helper.getWritableDatabase();

    // 创建一个ContentValues对象,将参数以key,value的形式添加进去
    ContentValues values=new ContentValues();
    values.put("name", name);
    values.put("number", number);

    // 插入一条数据到person表中
    long id=db.insert("person", null, values);

    // 关闭数据库
    db.close();
    return id;
}

除了上述介绍的方法之外,还有一个方法可以实现该功能:

db.execSQL("insert into person(name, number) values (?, ?)", new Object[]{name, number});

需要注意的是,使用完SQLiteDatabase对象后一定要关闭,否则数据库连接会一直存在,不断消耗内存,并且会报出数据库未关闭异常,当系统内存不足时将获取不到SQLiteDatabase对象。

(3) 删

示例代码:使用SQLiteDatabase对象的delete()方法删除person表中的数据

public int delete(String name){
    // 获取一个可读写的SQLiteDatabase对象
    SQLiteDatabase db=helper.getWritableDatabase();

    // 删除数据
    int number=db.delete("person", "name=?", new String[]{name});

    // 关闭数据库
    db.close();
    return number;
}

(4) 改

示例代码:使用SQLiteDatabase对象的update()方法修改person表中的数据

public int update(String name, String newNumber) {
    // 获取一个可读写的SQLiteDatabase对象
    SQLiteDatabase db=helper.getWritableDatabase();

    // 创建一个ContentValues对象,将参数以key,value的形式添加进去
    ContentValues values=new ContentValues();
    values.put("number", newNumber);

    //执行修改
    int number=db.update("person", values, "name=?", new String[]{name});

    // 关闭数据库
    db.close();
    return number;
}

(5) 查

SQLiteDatabase提供两个用于查询数据的方法,一个是rawQuery()方法,另一个是query()方法。

示例代码:使用SQLiteDatabase对象的query()方法查询person表中的数据

public boolean find(String name){
    // 获取一个可读的SQLiteDatabase对象
    SQLiteDatabase db=helper.getReadableDatabase();

    // 查询数据库的操作
    // 参数1:表名;  参数2:查询的列名;  参数3:查询条件;
    // 参数4:查询参数值;  参数5:分组条件;  参数6:having条件;  参数7:排列方式
    Cursor cursor=db.query("", null, "", new String[]{name}, null, null, null);
    // 是否有下一个值
    boolean result=cursor.moveToNext();
    // 关闭游标
    cursor.close();

    // 关闭数据库
    db.close();
    return result;
}

使用SQLiteDatabase对象的rawQuery()方法查询person表中的数据

Cursor cursor=db.rawQuery("select * from person where name=?", new String[]{name});

增、删、改操作都可以用execSQL()方法执行SQL语句,而查询却使用rawQuery(),这是因为查询数据库会返回一个结果集Cursor,而execSQL()方法没有返回值。

需要注意的是,在使用完Cursor对象时,一定要及时关闭,否则会造成内存泄露。

3. SQLite事务操作

所谓的事务就是针对数据库的一组操作,它可以由一条或多条SQL语句组成,由于事务的操作具备原子性的特点,如果其中有一条语句无法执行,那么所有的语句都不会执行,也就是说,事务中的语句要么都执行,要么都不执行。

示例代码:使用SQLite的事务来模拟转账功能

首先得到一个SQLiteDatabase对象,然后开启事务执行事务操作,最后关闭事务。

PersonSQLiteOpenHelper helper=new PersonSQLiteOpenHelper(getContext());
// 获取一个可读写的SQLiteDatabase对象
SQLiteDatabase db=helper.getWritableDatabase();
// 开始数据库的事务
db.beginTransaction();
try{    
    //执行转出操作
    db.execSQL("update person set account=account-100 where name=?", new Object[]{"zhangsan"}); 
    //执行转入操作
    db.execSQL("update person set account=account+100 where name=?", new Object[]{"wangwu"}); 
    //标记数据库事物执行成功
    db.setTransactionSuccessful();
}catch(Exception e){
    Log.i("事务处理失败", e.toString());
}finally{
    db.endTransaction();  //关闭事务
    db.close();           //关闭数据库
}

需要注意的是,事务操作完成后,一定要使用endTransaction()方法关闭事务,当执行到endTransaction()方法时,首先会检查是否有事务执行成功的标记,有则提交数据,无则回滚数据。最后会关闭事务,如果不关闭事务,事务只有到超时才自动结束,会降低数据库并发效率。因此,通常情况下,该方法放在finally中执行。

4. sqlite3工具

在Android开发中,使用真机进行测试无法进入data目录(只有获得root权限的手机可以进入data目录),因此也无法直接操作应用程序下的数据库。
为了解决这个问题,SQLite数据库为开发者提供了一个sqlite3.exe工具,通过这个工具可以直接操作数据库。

sqlite3.exe是一个简单的SQLite数据库管理工具,位于Android sdk/tools目录下。由于Android在运行时集成了sqlite3.exe,因此在使用sqlite3.exe工具前必须要开启模拟器或者连接真实设备。

使用该工具时,需要在命令行,一次输入如下命令:

  • adb shell (挂载到Linux的目录空间)
  • cd data/data (进入data/data目录)
  • cd packagename (进入应用程序包目录中)
  • ls (列出当前文件夹下的文件)
  • cd databases (进入databases文件夹)
  • ls -l (列出当前文件夹所有文件的详细格式)
  • sqlite3 person.db (使用sqlite3操作应用程序下的数据库)
  • select * from person; (使用SQL语句查询person表中的信息)

三、ListView

在Android开发中,ListView是一个比较常用的控件。它以列表的形式展示具体数据内容,并且能够根据数据的长度自适应屏幕显示。

1. ListView控件的使用

ListView的常见属性如图5-4所示。


图 5-4

使用ListView的示例代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ll_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

图5-5

图形化界面如图5-5所示,ListView是一个列表视图,由很多Item组成,每一个Item的布局都是一样的。需要注意的是,在布局文件中指定了ListView的id之后,ListView才会在图形化视图中显示出来。同时如果不对ListView进行数据适配,那么就无法在界面上看到布局文件中创建的ListView。

2. 常用数据适配器

在使用ListView时,需要对其进行数据适配。为了实现这个功能,Android系统提供了一系列的适配器(Adapter)对ListView进行数据适配。可以将适配器理解为界面数据绑定,适配器就像显示器,把复杂的数据按人们易于接受的方式来展示。

(1) BaseAdapter

它是一个抽象类,该类拥有4个抽象方法。在Android开发中,开发者在适配数据到ListView时,需要创建一个类继承BaseAdapter并重写这4个抽象方法,Android系统再根据这几个抽象方法对ListView进行数据适配。

  • public int getCount(); 得到ListView中Item的总数
  • public Object getItem(int position); 根据position得到某个Item对象
  • public long getItemId(int position); 根据position得到某个Item对象的id
  • public View getView(int position, View convertView, ViewGroup parent); 得到position位置对应的Item视图

(2) SimpleAdapter

SimpleAdapter继承自BaseAdapter,实现了BaseAdapter的4个抽象方法。因此,开发者只需在创建SimpleAdapter实例时,在构造方法中传入相应的参数即可。

SimpleAdapter的构造方法:

public SimpleAdapter(Context context, List<? extends Map<String, ?> data, int resource, String[] from, int[] to);

构造方法的参数介绍如下:

  • Context context:Context对象,getView()方法中需要用到Context将布局转换成View对象
  • List<? extends Map> data:数据集合,SimpleAdapter已经在getCount()放啊中实现了将数据集合大小返回。
  • int resource:Item布局的资源id
  • String[] from:Map集合里面的key
  • int[] to:Item布局对应的空间id

需要注意的是,SimpleAdapter只能适配Checkable、TextView、ImageView,其中Checkable是一个接口,CheckBox控件就实现了该接口,TextView是显示文本的控件,ImageView是显示图片的空间。如果int[] to所代表的控件不是这三种类型则会报IllegalStateException。

(3) ArrayAdapter

ArrayAdapter也是BaseAdapter的子类,与SimpleAdapter类似,开发者只需要在构造方法里面传入相应的参数即可适配数据。

// 需要适配的数据
private String[] names = { "京东商城", "QQ", "QQ斗地主", "新浪微博", "天猫", "UC浏览器", "微信" };

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // 初始化ListView控件
    mListView = (ListView) findViewById(R.id.lv);

    // ArrayAdapter
    mListView.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item, R.id.tv_list, names));
}

ArrayAdapter通常用于适配TextView控件,例如Android系统中的Setting。

ArrayAdapter的构造方法如下所示:public ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects);

构造方法的参数介绍如下:

  • Context context:Context对象
  • int resource:Item布局资源的id
  • int textViewResourceId:Item布局相应的TextView控件的id
  • T[] objects:需要适配的数据数组

案例:Demo0501,Android应用市场,演示如何使用ListView以及如何对其进行数据适配。本案例要实现的是将一个字符数组和一组图片资源绑定到ListView上显示。

案例:Demo0502,商品展示,结合ListView和SQLite来实现在界面上操作数据库。

ListView的几个方法:

  • setOnItemClickListener()方法:该方法用于监听Item的点击事件,在使用该方法时需要传入一个OnItemClickListener的实现类对象,并且需要实现onItemClick方法。当点击ListView的Item时就会触发Item的点击事件然后回调onItemClick()方法。
  • setSelection()方法:该方法用于设置当前选中的条目。
  • notifyDataSetChange()方法:当数据适配器中的内容发生变化时会调用该方法,重新执行Adapter的getView()方法。
分享到 评论