SQLite数据库简单介绍
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百K的内存就足够了,因而特别适合在移动设备上使用。 SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。 它甚至不用设置用户名和密码就能使用,可以快速上手。 前面所学的文件存储和SharedPreferences存储毕竟只能适用于去保存一些简单的数据和键值对,当需要存储大量复杂的关系型数据的时候,就需要使用SQLite数据库,用来存储这些数据量大、结构性复杂的数据。
创建数据库
SQLiteOpenHelper类
简单概述
SQLiteOpenHelper类可以非常简单地对数据库进行创建和升级。 SQLiteOpenHelper类是一个抽象类,如果需要使用它,需要创建一个类去实现(继承)它。 SQLiteOpenHelper类中有两个抽象方法,分别是
onCreate()方法 onUpgrade() 方法 我们必须在自己创建的类中去重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。 SQLiteOpenHelper 类还有两个非常重要的实例方法
getReadableDatabase()方法 getWritableDatabase()方法 这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式打开数据库,而getWritableDatabase()方法则将出现异常。 SQLiteOpenHelper 类中有两个构造方法可供重写,一般使用参数少一点的那个构造方法即可。
第一参数是Context,这个没什么好说的,必须要有它才能对数据库进行操作。 第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。 第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null。 第四个参数表示我们当前数据库的版本号,可用于对数据库进行升级操作。 构建出SQLiteOpenHelper 的实例之后,在调用它的getReadableDatabase()或getWritableDatabase()方法就能够创建数据库了,数据库文件的存放目录,可以看我上篇博文的介绍 Android Studio中查看SQlite数据库和表的创建情况 。此时,重写的onCreate()方法也会得到执行,所以通常这里会去处理一些创建表的逻辑。
DatabaseTest项目
创建一个名为BookStore.db的数据库,然后在这个数据库中新建一张Book表,表中有id(主键)、作者、价格、页数和书名等列。创建数据库表当然还是需要用建表语句的,Book表的建表语句如下所示:
private static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)" ;
新建MyDatabaseHelper类继承自SQLiteOpenHelpler
package com. example. myapplication;
import android. content. Context;
import android. database. sqlite. SQLiteDatabase;
import android. database. sqlite. SQLiteOpenHelper;
import android. widget. Toast;
import androidx. annotation. Nullable;
public class MyDatabaseHelper extends SQLiteOpenHelper {
private Context mContext;
private static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)" ;
public MyDatabaseHelper ( @Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase. CursorFactory factory, int version) {
super ( context, name, factory, version) ;
mContext = context;
}
@Override
public void onCreate ( SQLiteDatabase db) {
db. execSQL ( CREATE_BOOK) ;
Toast. makeText ( mContext, "Create Succeeded!" , Toast. LENGTH_LONG) . show ( ) ;
}
@Override
public void onUpgrade ( SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
修改activity_main.xml中的代码,如下所示:
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns: android= " http://schemas.android.com/apk/res/android"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
android: orientation= " vertical" >
< Button
android: id= " @+id/create_database"
android: layout_width= " match_parent"
android: layout_height= " wrap_content"
android: text= " Create Database"
/>
</ LinearLayout>
package com. example. myapplication;
import androidx. appcompat. app. AppCompatActivity;
import android. annotation. SuppressLint;
import android. content. ContentValues;
import android. database. Cursor;
import android. database. sqlite. SQLiteDatabase;
import android. database. sqlite. SQLiteOpenHelper;
import android. os. Bundle;
import android. util. Log;
import android. view. View;
import android. widget. Button;
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbOpenHelper;
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
dbOpenHelper = new MyDatabaseHelper ( this , "BookStore.db" , null, 1 ) ;
Button createDatabase = ( Button) findViewById ( R. id. create_database) ;
createDatabase. setOnClickListener ( new View. OnClickListener ( ) {
@Override
public void onClick ( View view) {
dbOpenHelper. getWritableDatabase ( ) ;
}
} ) ;
}
}
升级数据库
onUpgrade()方法是用于对数据库进行升级的,它在整个数据库的管理当中起着非常重要的作用。 目前数据库中已经有一张Book表存放书籍的各种详细信息,如果我们想再添加一张Category表用于记录书籍的分类该怎么做呢? 比如Category表中有id(主键)、分类名和分类代码这几个列。 将建表语句添加到MyDatabaseHelper类中,代码如下:
public class MyDatabaseHelper extends SQLiteOpenHelper {
private Context mContext;
private static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)" ;
private static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)" ;
public MyDatabaseHelper ( @Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase. CursorFactory factory, int version) {
super ( context, name, factory, version) ;
mContext = context;
}
@Override
public void onCreate ( SQLiteDatabase db) {
db. execSQL ( CREATE_BOOK) ;
db. execSQL ( CREATE_CATEGORY) ;
Toast. makeText ( mContext, "Create Succeeded!" , Toast. LENGTH_LONG) . show ( ) ;
}
@Override
public void onUpgrade ( SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
解决上面的问题很简单,只需要先将程序卸载掉,对应的就是 清除模拟器中的数据(Wipe Data) ,然后重新运行,这时BookStore.db数据库就已经不存在了,如果再点击Create Database按钮,MyDatabaseHelper中的onCreate()方法就会执行,这时Category表就可以创建成功了。 不过通过卸载数据库的方法来新增一张表毫无疑问是很极端的方法,其实我们只需要巧妙地运用SQLiteOpenHelper的升级功能就可以很轻松地解决这个问题。修改MyDatabaseHelper中的代码,如下所示:
public class MyDatabaseHelper extends SQLiteOpenHelper {
@Override
public void onUpgrade ( SQLiteDatabase db, int oldVersion, int newVersion) {
db. execSQL ( "drop table if exists Book" ) ;
db. execSQL ( "drop table if exists Category" ) ;
onCreate ( db) ;
}
}
接下来的问题就是如何让onUpgrade()方法能够执行了,还记得SQLiteOpenHelper的构造方法里接收的第四个参数吗?它表示当前数据库的版本号,之前我们传入的是1,现在只要传入一个比1大的数,就可以让onUpgrade()方法得到执行了。修改MainActivity中的代码,如下所示:
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbOpenHelper;
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
dbOpenHelper = new MyDatabaseHelper ( this , "BookStore.db" , null, 2 ) ;
Button createDatabase = ( Button) findViewById ( R. id. create_database) ;
createDatabase. setOnClickListener ( new View. OnClickListener ( ) {
@Override
public void onClick ( View view) {
dbOpenHelper. getWritableDatabase ( ) ;
}
} ) ;
}
}
对表中的数据进行操作
对数据进行的操作无非四种,即CRUD。其中C代表添加,Create;R代表查询,Retrieve;U代表更新,Update;D代表删除,Delete。每一种操作又各自对应了一种SQL命令,添加数据时使用insert,查询数据时使用select,更新数据时使用update,删除数据时使用delete。但是除了SQL命令之外,Android还提供了一套辅助性方法。 调用SQLiteOpenHelper的getReadableDatabase()或getWritableDatabase()方法是可以用于创建和升级数据库的,不仅如此,这两个方法都会返回一个SQLiteDatabse对象,借助这个对象我们就可以对数据进行CRUD操作了。
添加数据
SQLiteDatabase中提供了一个insert()方法,这个方法就是专门用来添加数据的。
它接收三个参数,第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。 第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可。 第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。 举例说明,修改activity_main.xml中的代码,如下所示。
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns: android= " http://schemas.android.com/apk/res/android"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
android: orientation= " vertical" >
......
< Button
android: layout_width= " match_parent"
android: layout_height= " wrap_content"
android: id= " @+id/add_data"
android: text= " Add Data"
/>
</ LinearLayout>
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbOpenHelper;
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
dbOpenHelper = new MyDatabaseHelper ( this , "BookStore.db" , null, 2 ) ;
. . . . . .
Button addData = ( Button) findViewById ( R. id. add_data) ;
addData. setOnClickListener ( new View. OnClickListener ( ) {
@Override
public void onClick ( View view) {
SQLiteDatabase db = dbOpenHelper. getWritableDatabase ( ) ;
ContentValues values = new ContentValues ( ) ;
values. put ( "author" , "Dan Brown" ) ;
values. put ( "price" , 19.6 ) ;
values. put ( "pages" , 454 ) ;
values. put ( "name" , "The Da Vinci Code" ) ;
db. insert ( "Book" , null, values) ;
values. put ( "author" , "Dan Brown" ) ;
values. put ( "price" , 19.95 ) ;
values. put ( "pages" , 550 ) ;
values. put ( "name" , "The Lost Symbol" ) ;
db. insert ( "Book" , null, values) ;
}
} ) ;
}
}
接下来运行程序,界面和adb工具查询结果如下:
界面 adb查询结果
更新数据
SQLiteDatabase提供了一个update()方法用于对数据进行更新
第一个参数,表名,在这里指定去更新哪张表里的数据 第二个参数,ContentValues对象,要把更新的数据在这里组装进去。 第三、四个参数,用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有的行。 在以上数据库和表中进行修改:将添加进数据库里的第一本书的价格降低,来吸引更多的顾客。 修改activity_main.xml中的代码,如下所示。
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns: android= " http://schemas.android.com/apk/res/android"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
android: orientation= " vertical" >
......
< Button
android: layout_width= " match_parent"
android: layout_height= " wrap_content"
android: id= " @+id/update_data"
android: text= " Update Data"
/>
</ LinearLayout>
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbOpenHelper;
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
dbOpenHelper = new MyDatabaseHelper ( this , "BookStore.db" , null, 2 ) ;
. . . . . .
Button updateData = ( Button) findViewById ( R. id. update_data) ;
updateData. setOnClickListener ( new View. OnClickListener ( ) {
@Override
public void onClick ( View view) {
SQLiteDatabase db = dbOpenHelper. getWritableDatabase ( ) ;
ContentValues values = new ContentValues ( ) ;
values. put ( "price" , 10.99 ) ;
db. update ( "Book" , values, "name = ?" , new String [ ] { "The Da Vinci Code" } ) ;
}
} ) ;
}
}
重新运行程序,界面如下所示。 点击Update Data按钮,再次在adb查询工具查询表中的数据情况,如下所示。
删除数据
SQLiteDatabase中提供了一个delete()方法专门用于删除数据
第一个参数,表名 第二、三个参数,用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。 修改activity_main.xml中的代码,如下所示。
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns: android= " http://schemas.android.com/apk/res/android"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
android: orientation= " vertical" >
......
< Button
android: layout_width= " match_parent"
android: layout_height= " wrap_content"
android: id= " @+id/delete_data"
android: text= " Delete Data"
/>
</ LinearLayout>
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbOpenHelper;
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
dbOpenHelper = new MyDatabaseHelper ( this , "BookStore.db" , null, 2 ) ;
. . . . . .
Button deleteData = ( Button) findViewById ( R. id. delete_data) ;
deleteData. setOnClickListener ( new View. OnClickListener ( ) {
@Override
public void onClick ( View view) {
SQLiteDatabase db = dbOpenHelper. getWritableDatabase ( ) ;
db. delete ( "Book" , "pages > ?" , new String [ ] { "500" } ) ;
}
} ) ;
}
}
界面如下所示。 adb查询工具查询结果如下所示。
查询数据
SQLiteDatabase中提供了一个query()方法用于对数据进行查询,这里介绍最少参数(7个参数)的方法重载。调用query()方法后会返回一个Cursor对象,各参数含义如下表所示。
query()方法参数 对应SQL部分 描述 table from table_name 指定查询的表名 columns select column1,column2 指定查询的列名 selection where column=value 指定where的约束条件 selectionArgs - 为where中的占位符提供具体的值 groupBy group by column 指定需要group by的列 having having column = value 对group by后的结果进一步约束 orderBy order by column1,column2 指定查询结果的排序方式
第二个参数,如果不指定默认查询所有列。 第三、四个参数,不指定则默认是查询所有行的数据。 第五个参数,不指定则表示不对查询结果进行group by的操作。 第六个参数,不指定则表示不进行约束。 第七个参数,不指定则表示使用默认的排序方式。 其他几个query()方法的重载其实也大同小异。 修改activity_main.xml中的代码,如下所示。
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns: android= " http://schemas.android.com/apk/res/android"
android: layout_width= " match_parent"
android: layout_height= " match_parent"
android: orientation= " vertical" >
......
< Button
android: layout_width= " match_parent"
android: layout_height= " wrap_content"
android: id= " @+id/query_data"
android: text= " Query Data"
/>
</ LinearLayout>
package com. example. myapplication;
import androidx. appcompat. app. AppCompatActivity;
import android. annotation. SuppressLint;
import android. content. ContentValues;
import android. database. Cursor;
import android. database. sqlite. SQLiteDatabase;
import android. database. sqlite. SQLiteOpenHelper;
import android. os. Bundle;
import android. util. Log;
import android. view. View;
import android. widget. Button;
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbOpenHelper;
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
dbOpenHelper = new MyDatabaseHelper ( this , "BookStore.db" , null, 2 ) ;
. . . . . .
Button queryData = ( Button) findViewById ( R. id. query_data) ;
queryData. setOnClickListener ( new View. OnClickListener ( ) {
@Override
public void onClick ( View view) {
SQLiteDatabase db = dbOpenHelper. getWritableDatabase ( ) ;
Cursor cursor = db. query ( "Book" , null, null, null, null, null, null) ;
if ( cursor. moveToFirst ( ) ) {
do {
@SuppressLint ( "Range" ) String author = cursor. getString ( cursor. getColumnIndex ( "author" ) ) ;
@SuppressLint ( "Range" ) double price = cursor. getDouble ( cursor. getColumnIndex ( "price" ) ) ;
@SuppressLint ( "Range" ) int pages = cursor. getInt ( cursor. getColumnIndex ( "pages" ) ) ;
@SuppressLint ( "Range" ) String name = cursor. getString ( cursor. getColumnIndex ( "name" ) ) ;
Log. d ( "MainActivity" , author) ;
Log. d ( "MainActivity" , "" + price) ;
Log. d ( "MainActivity" , "" + pages) ;
Log. d ( "MainActivity" , name) ;
} while ( cursor. moveToNext ( ) ) ;
}
cursor. close ( ) ;
}
} ) ;
}
}
界面如下所示。 点击一下Query Data按钮,查看LogCat的打印内容,结果如下所示。
使用SQL操作数据库
直接使用SQL来完成前面学过的CRUD(C代表添加,Create;R代表查询,Retrieve;U代表更新,Update;D代表删除,Delete)
db. execSQL ( "insert into Book (name, author, pages, price) values(?, ?, ?, ?)" , new String [ ] { "The Da Vinci Code" , "Dan Brown" , "454" , "16.96" } ) ;
db. execSQL ( "insert into Book (name, author, pages, price) values(?, ?, ?, ?)" , new String [ ] { "The Lost Symbol" , "Dan Brown" , "510" , "19.95" } ) ;
db. execSQL ( "update Book set price = ? where name = ?" , new String [ ] { "10.99" , "The Da Vinci Code" } ) ;
db. execSQL ( "delete from Book where pages > ?" , new String [ ] { "500" } ) ;
db. execSQL ( "select * from Book" , null) ;