前言

Android中存储数据的方式有很多种,其中使用SQLite数据库是存储结构化数据的最佳选择。幸运的是,Android中默认提供了对SQLite的支持,这就使得在Android中使用SQLite数据库变得格外方便。

支持的数据类型

SQLite是一款轻量级的数据库,其支持的数据类型也很简单,主要有以下几种:

  • text:字符类型
  • real:浮点类型
  • integer:整数类型
  • blob:二进制数据类型

创建数据库

SQLite数据库的使用始于SQLiteOpenHelper这个抽象类。我们需要自定义类去继承SQLiteOpenHelper,并重写它的构造方法。实际上,有两个构造方法可以重写,一般我们选择重写参数较少的那个方法。

此外,我们还需要实现onCreateonUpgrade这两个抽象方法。在onCreate方法中,我们对数据库进行创建。而在onUpgrade方法中,我们则对数据库进行升级、更新。基本代码如下:

 public class BookOpenHelper extends SQLiteOpenHelper{    private static final String CREATE_BOOK="create table Book(" +            "id integer primary key autoincrement," +            "name text," +            "price text," +            "author text)";    public BookOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);    }    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(CREATE_BOOK);    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    }}

可以看到,在构造方法中,我们只是简单地调用了父类的构造方法。在构造方法的参数列表中,第一个参数是Context对象,第二个参数是数据库名称,第三个参数一般传入null,第四个参数是数据库的版本号。

此外,我们定义了一个字符串常量。很明显,这是一条建表语句,用于创建名为Book的表。在这个表中,我们定义了四个字段,分别是id、name、price、author。其中,idprimary keyautoincrement修饰,代表它是表的主键且自增。在onCreate方法中,我们调用了SQLiteDatabaseexecSQL方法,执行Book表的建表语句。

在实际使用中,我们需要先构建SQLiteOpenHelper的实例,然后调用它的getReadableDatabase或者getWritableDatabase方法,如果数据库还未创建,系统就会创建数据库并调用SQLiteOpenHelperonCreate方法。实例代码如下:

BookOpenHelper dbOpenHelper=new BookOpenHelper(            MainActivity.this,"SQLiteDemo.db",null,1);dbOpenHelper.getReadableDatabase();

getReadableDatabasegetWritableDatabase方法都会返回一个可以对数据库进行增删改查操作的SQLiteDatabase对象。它们的区别在于,当手机存储空间已满的时候,前者会返回null,而后者会抛出异常。

更新数据库

在实际开发中,常常需要对数据库进行修改,比如添加新的表或是增加表字段等。为了实现对数据库的更新,我们需要在构建SQLiteOpenHelper实例的时候传入一个比当前版本号更大的version参数,这将导致SQLiteOpenHelperonUpgrade方法被调用。在这个方法中,我们就可以进行数据库的更新操作了。以下代码演示在之前的数据库中添加名为Author的表:

public class BookOpenHelper extends SQLiteOpenHelper{    .......    //创建Author表    private static final String CREATE_AUTHOR="create table Author(" +            "id integer primary key autoincrement," +            "name text," +            "age integer)";    public BookOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);    }    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(CREATE_BOOK);        db.execSQL(CREATE_AUTHOR);    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        switch (oldVersion){            case 1:                db.execSQL(CREATE_AUTHOR);            default:                break;        }    }}

我们定义了一个新的字符串常量CREATE_AUTHOR,用于创建Author表。同时,我们也在onCreate方法中执行了db.execSQL(CREATE_AUTHOR);。如果应用是首次安装,就可以正常地创建两张表。但是,如果应用之前已经被安装了。由于数据库已经存在,onCreate方法就不会被执行了。在这种情况下,我们将补救措施写在了onUpgrade方法中。通过对oldVersion、即旧版本号的判断,我们执行了后续的建表语句。只要在创建SQLiteOpenHelper的时候传入了比当前版本号更大的version参数,onUpgrade方法就会被调用,Author表也就会被创建了。

如果现在我们又想要为Author表添加一个新的字段country,用于记录作者的名字,又该如何操作呢?实际上做法和前面一样,只需要在onUpgrade方法中执行更新操作即可,具体代码如下:

public class BookOpenHelper extends SQLiteOpenHelper{    .......    //为Author表添加新的字段    private static final String ALTER_AUTHOR="alter table Author add column country text";    public BookOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);    }    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(CREATE_BOOK);        db.execSQL(CREATE_AUTHOR);        db.execSQL(ALTER_AUTHOR);    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        switch (oldVersion){            case 1:                db.execSQL(CREATE_AUTHOR);            case 2:                db.execSQL(ALTER_AUTHOR);            default:                break;        }    }}

onCreate方法中,我们依次执行了各个SQL操作,这样当应用首次安装时就可以正常地创建所有的表。而在onUpgrade方法中,我们通过判断当前数据库的版本号,来决定应该做哪些更新操作。若当前版本号为2,则只需要为Author表添加新的字段;若当前版本号为1,则需要先创建Author表,再为Author表添加新的字段。这也是为什么每个case都不加break的原因,这样才能保证应用跨版本升级的时候能够依次执行所有的更新操作。这一点尤其需要注意。

基本操作

解决了如何创建数据库和更新数据库的问题,接下来就可以正式接触SQLite中的各项数据操作了。一般可以简称为CRUD操作,即增删改查。首先,我们需要获得一个SQLiteDatabase对象,它封装了多种数据操作方法。可以通过SQLiteOpenHelper实例的getReadableDatabasegetWritableDatabase方法获得这个对象。

SQLiteDatabase database=dbOpenHelper.getWritableDatabase();SQLiteDatabase database=dbOpenHelper.getReadableDatabase();

添加数据

SQLiteDatabase通过insert方法实现数据的添加,其方法原型如下:

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

insert方法需要传入三个参数,第一个参数是表名,第二个参数一般传入null,第三个参数是一个ContentValues对象。ContentValues通过键值对的方式存储需要写入表中的数据。插入完成后,这个方法还会返回插入数据在表中的id号,如果插入失败,则返回-1。示例代码如下:

SQLiteDatabase database= dbOpenHelper.getWritableDatabase();ContentValues values=new ContentValues();values.put("name","Android");values.put("author","Bill");values.put("price","60");database.insert("Book",null,values);values.clear();values.put("name","Java");values.put("author","Tom");values.put("price","50");database.insert("Book",null,values);

可以看到,我们通过同一个ContentValues对象向Book表添加了两条数据。需要注意的是,在添加第二条数据之前应该先调用ContentValuesclear方法清除旧数据。

查询数据

SQLiteDatabase通过query方法实现数据的添加,常用版本的方法原型如下:

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

query方法需要传入7个参数。第一个参数是表名。第二个参数是需要查询的列名,如果传入null则代表查询所有列。第三个参数和第四个参数是查询条件,用于限制查询的行,相当于SQL中的where部分,如果都传入null表示查询所有行。第五参数是分组的列名,相当于SQL中的group by部分,如果传入null表示不对结果进行分组。第六个参数则相当于SQL中的having部分。第七个参数是排序的列名,传入null表示对查询结果进行默认排序。

这个方法在查询完成后会返回Cursor对象,这是一个游标,可以通过遍历的方式去取出查询到的数据。示例代码如下:

SQLiteDatabase database= dbOpenHelper.getReadableDatabase();Cursor cursor=database.query("Book",null,null,null,null,null,null);if(cursor.moveToFirst()){    do {        Log.d(TAG,cursor.getString(cursor.getColumnIndex("id")));        Log.d(TAG,cursor.getString(cursor.getColumnIndex("name")));        Log.d(TAG,cursor.getString(cursor.getColumnIndex("author")));        Log.d(TAG,cursor.getString(cursor.getColumnIndex("price")));    }    while(cursor.moveToNext());}cursor.close();

我们仅为query方法传入了一个表名,其他参数均传入null,表示查询Book表中的所有数据。对于这个方法返回的Cursor对象,我们先调用moveToFirst方法移动到数据的第一行。随后,在一个do-while循环中判断cursormoveToNext方法是否为true,并通过getString方法取出表中的数据。getColumnIndex方法用于获取表中指定列名对应的索引号。这是一个最简单的查询操作,现在我们再尝试一次限制条件更多的查询:

SQLiteDatabase database= dbOpenHelper.getReadableDatabase();Cursor cursor=database.query("Book",new String[]{"name","author"},                    "author=?",new String[]{"Tom"},null,null,"name");if(cursor.moveToFirst()){    do {        Log.d(TAG,cursor.getString(cursor.getColumnIndex("name")));        Log.d(TAG,cursor.getString(cursor.getColumnIndex("author")));    }    while(cursor.moveToNext());}cursor.close();

以上代码的目的是查询Book表中authorTom的数据,并且只查询nameauthor这两列的数据,最后将返回结果根据name进行排序。需要注意的是,在完成查询后,我们还应该调用Cursorclose方法,释放资源。

更新数据

SQLiteDatabase通过query方法实现数据的添加,其方法原型如下:

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

update方法需要传入4个参数。第一个参数是表名。第二个参数是ContentValues对象,存储更新后的数据。第三个参数和第四个参数是限制条件,都传入null就代表更新表中的所有数据。更新完成后,这个方法会返回受影响的行数。示例代码如下:

SQLiteDatabase database= dbOpenHelper.getWritableDatabase();ContentValues values=new ContentValues();values.put("name","Android Plus");database.update("Book",values,"name=?",new String[]{"Android"});

以上代码查询Book表中所有nameAndroid的数据,并将name字段更新为Android Plus。需要注意的是,我们只需要在ContentValues中存储需要更新的数据,不需要存储其他不变的数据。

删除数据

SQLiteDatabase通过delete方法实现数据的删除,其方法原型如下:

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

delete方法需要传入三个参数。第一个参数是表名。第二个和第三个参数是限制条件,如果都传入null就表示删除表中所有的数据。示例代码如下:

SQLiteDatabase database=dbOpenHelper.getWritableDatabase();database.delete("Book","name=?",new String[]{"Java"});

以上代码将会删除Book表中所有nameJava的数据。

使用SQL操作数据库

除了使用SQLiteDatabase封装好的insert、delete、update、query方法对数据库进行增删改查,我们还可以直接使用SQL语句对数据进行操作。当然,前提是SQL基础要足够好。SQLiteDatabase提供了execSQLrawQuery这两个方法用于直接执行SQL语句。不同之处在于,后者用于执行查询操作,前者则执行其他操作。这两个方法常用版本的原型如下:

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

示例代码如下:

//插入数据database.execSQL("insert into Book(name,price,author) values(?,?,?)",new String[]{"book","110","Bill"});//查询数据Cursor cursor=database.rawQuery("select * from Book",null);

使用事务

在SQLiteDatabase中,Android还为开发者集成了事务功能。那么什么是事务呢?想象这样一个场景,由于应用的版本更新,我们需要删除一部分数据,然后插入新的数据。但是要求删除操作和插入操作必须一起成功,否则就一起失败。这样做可以避免在删除数据后,由于某些原因应用出错,导致插入操作没能完成,而影响用户的正常使用。使用事务,就可以顺利地完成这一要求。示例代码如下:

SQLiteDatabase database=dbOpenHelper.getWritableDatabase();database.beginTransaction();try{    database.delete("Book","name=?",new String[]{"Android"});    if(true){//手动抛出异常演示在执行事务的过程中操作失败        throw new Exception("手动抛出异常");    }    ContentValues values=new ContentValues();    values.put("name","Java");    values.put("author","Jimmy");    values.put("price","500");    database.insert("Book",null,values);    database.setTransactionSuccessful();}catch(Exception e){    Log.d(TAG,"执行事务过程中出现错误!");    e.printStackTrace();}finally {    database.endTransaction();}

可以看到,我们首先调用了SQLiteDatabasebeginTransaction方法开启事务。然后在try语句块中,我们先是删除了Book表中所有nameAndroid的数据,随后向Book中插入了一条新的数据。在try语句块的最后,我们调用了SQLiteDatabasesetTransactionSuccessful方法,表示事务成功执行。但是在删除数据后,我们手动抛出了一个异常,这将会导致后续的添加操作失败。如果是在普通情况下,最后的结果就是数据被删除了,但是新的数据却没有添加成功。而在这里使用了事务,出现异常后,也将会导致前面的删除操作失败。需要注意的是,我们还需要在finally语句块中调用endTransaction方法,表示事务结束。

总结一下,使用事务的关键就是下面这三句,需要使用事务的时候参照上述写法即可。

database.beginTransaction();database.setTransactionSuccessful();database.endTransaction();

再次强调:如果开启了事务,一定要记得调用setTransactionSuccessfulendTransaction方法,否则在事务中对数据库执行的操作都不会生效。

使用开源库操作数据库

除了通过本文讲解的方式操作SQLite数据库外,我们还可以借助开源库的力量。比如LitePal、GreenDAO、ActiveAndroid、realm-java、sqlbrite等。对于这些开源库的使用,可以参考它们的Github主页,也可以参考下列博客:

使用LitePal操作数据库(先挖坑,随后填)

项目demo下载地址

下面给出上述例子的demo下载地址:SQLiteDemo

更多相关文章

  1. Android不同版本获取当前wifi信息方法
  2. android动态加载已安装apk中的方法
  3. Android的SQLite学习及使用方法(1)
  4. Android 判断用户2G/3G/4G移动数据网络
  5. android数据库 Android自带数据库SQLite 操作 一步一步 图示教程
  6. Android Studio中使用adb shell查看数据库
  7. 共享全局数据 android
  8. FastBoot 刷机方法

随机推荐

  1. Android(安卓)播放Gif 动画
  2. R文件没了怎么办
  3. 九大角度全方位对比Android、iOS开发
  4. Unity3D 调用 Android(安卓)jar 包制作方
  5. android 连接远程数据库(转)
  6. 展望2011年Android发展前景
  7. Android事件处理方法总结-基于回调的事件
  8. Android图形系统的分析与移植--二、Andro
  9. android 初级篇:(一)漫谈android
  10. Android事件处理方法总结-基于回调的事件