Android学习之SQLite数据库存储
•引言
概念
SQLite数据库,和其他的SQL数据库不同, 我们并不需要在手机上另外安装一个数据库软件,Android系统已经集成了这个数据库;
特点
SQLite是一个轻量级的关系型数据库,运算速度快,占用资源少,很适合在移动设备上使用
不仅支持标准SQL语法,还遵循ACID(数据库事务)原则,无需账号,使用起来非常方便
SQLite支持五种数据类型
- NULL
- integer(整型)
- real(浮点型)
- text(文本类型)
- blob(二进制类型)
SQLite 通过文件来保存数据库
- 一个文件就是一个数据库
- 数据库中又包含多个表格
- 表格里又有多条记录
- 每条记录由多个字段构成
- 每个字段都有对应的值
•创建数据库
Android为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper 帮助类;
借助这个类就可以非常简单地对数据库进行创建和升级。
SQLiteOpenHelper 是一个抽象类,这意味着如果我们想要使用它的话,就需要创建一个自己的帮助类去继承它;
SQLiteOpenHelper 中有两个抽象方法,分别是 oncreate() 和 onUpgrade() ;
onCreate(database) : 数据库第一次被创建时被调用
onUpgrade(database,oldVersion,newVersion) : 在数据库的版本发生变化时会被调用
- 一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的
- 假设数据库现在的版本是 1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构
- 为了实现这一目的,可以把原来的数据库版本设置为 2,或者其他与旧版本号不同的数字即可
我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现 创建、升级数据库 的逻辑。
SQLiteOpenHelper 中还有两个非常重要的实例方法: getReadableDatabase() 和 getWritableDatabase() 。
这两个方法都可以 创建或打开 一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),
并返回一个可对数据库进行读写操作的对象。
不同的是,当数据库不可写入的时候(如磁盘空间已满):
- getReadableDatabase() 方法返回的对象将以只读的方式去打开数据库
- getWritableDatabase() 方法则将出现异常
SQLiteOpenHelper 中有三个构造方法可供重写,一般使用参数少一点的那个:
SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)
该构造方法中接收四个参数:
- Context context :这个没什么好说的,必须有这个才能对数据库进行操作
- String name :第二个参数是数据库名,创建数据库时使用的就是这里指定的名称
- SQLiteDatabase.CursorFactory factory :第三个参数允许我们在查询数据的时候返回一个自定义的 Cursor,一般都是传入null
- int version :第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作
构建出 SQLiteOpenHelper 的实例之后,再调用它的 getReadableDatabase() 或 getWritableDatabase() 方法就能够创建数据库了;
数据库文件会存放在 /data/data/<package name>/databases/ 目录下;
此时,重写的 onCreate() 方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。
•通过代码深入理解
准备工作
首先新建一个 TestDatabase 项目。
在该项目中新建 MyDatabaseHelper 类,继承自 SQLiteOpenHelper;
MyDatabaseHelper.java
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement," + "name text," + "price real)"; private Context mContext; 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_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }可以看到,我们把建表语句定义成了一个字符串常量,然后在 onCreate() 中调用了 db.execSQL(CREATE_BOOK); 去执行这条建表语句;
之后,弹出一个 Toast 提示建表成功,这样就可以保证在数据库创建的同时创建 Book 表;
在 SQL 中,Book 表的建表语句如下:
CREATE TABLE Book( id INTEGER PRIMARY KEY autoincrement, NAME TEXT, price REAL);其中,设置 id 为 primary key,并用 autoincrement 关键字表示 id 列是自增长的。
接下来,修改 activity_main.xml 中的代码;
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" android:padding="10dp"> <Button android:id="@+id/create" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Create" android:textAllCaps="false" /> </LinearLayout>布局文件很简单,就是加入了一个按钮,用于创建数据库。
最后,修改 MainActivity.java 中的代码;
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private MyDatabaseHelper helper; private Button mBtnCreate; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); helper = new MyDatabaseHelper(this,"BookStore.db",null,1); mBtnCreate = findViewById(R.id.create); mBtnCreate.setOnClickListener(this); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.create: helper.getWritableDatabase(); break; } } }这里我们在 onCreate() 方法中构建了一个 MyDatabaseHelper 对象,并且通过构造函数的参数将数据库名指定为 BookStore.db,版本号指定为 1;
然后在 Create按钮 的点击事件里调用了 getWritableDatabase() 方法;
这样当第一次点击 Create按钮 时,就会检测到当前程序中并没有 BookStore.db 这个数据库,
于是会创建该数据库并调用 MyDatabaseHelper 中的 onCreate() 方法,这样 Book 表也就得到了创建,然后会弹出一个 Toast 提示创建成功。
再次点击 Create按钮 时,会发现此时已经存在 BookStore.db 数据库了,因此不会再创建一次。
运行效果
此时 BookStore.db数据库 和 Book表 应该都已经创建成功了,因为当你再次点击 Create按钮 时,不会再有 Toast 弹出;
可是怎样才能证实它们的确创建成功了?
打开 Android Device Monitor(不知道什么是 ADM 的小伙伴可以参考我的这篇博客🔗),
找到 /data/data/com.example.testdatabase/databases/ 目录:
你会发现该目录下多了一个 BookStore.db 文件,但是看不到 Book 表,这可如何是好?
下面介绍一下通过使用 adb shell 方式来对数据库和表的创建情况进行检查。
通过 adb 调试
adb 是 Android SDK 中自带的一个调试工具,使用这个工具可以直接对连接在电脑上的手机或模拟器进行调试操作;
它存放在 Android SDK 的 platform-tools 目录下,如果想要在命令行中使用这个工具,就需要先把它的路径配置到环境变量中;
如果不知道你的 Android SDK 安装在哪的小伙伴,可以通过以下方式找到:
点击【Tools】->【SDK Manager】;
找到 Android SDK 的安装路径;
在【电脑】中打开该路径,并找到【platform-tools】文件夹;
复制该路径,打开【环境变量】,在【系统变量】里找到【Path】并点击编辑,将 platform-tools 目录配置进去即可;
配置好了环境变量后,就可以使用 adb 工具了;
通过【Win+R】打开命令行界面,输入 adb shell,就会进入到设备的控制台;
其中,# 符号是超级管理员的意思,也就是说,现在你可以访问模拟器中的一切数据;
如果你的命令行上显示的是 $,那么就表示你现在是普遍管理员,需输入 su 命令切换成超级管理员,这样才能执行以下操作;
接下来使用 cd 命令进入到 /data/data/com.example.testdatabase/databases/ 目录下;
(偷偷告诉你,鼠标右击可以实现粘贴功能)
使用 ls 命令查看该目录里的文件;
这个目录下出现了两个数据库文件,一个正是我们创建的 BookStore.db;
而另一个 BookStore.db-journal 则是为了让数据库能够支持事务而产生的临时日志文件,通常情况下这个文件的大小都是 0 字节;
接下来我们就要借助 sqlite 命令来打开数据库了,只需要键入【sqlite3 数据库名】即可;
这时就已经打开了 BookStore.db 数据库,现在就可以对这个数据库中的表进行管理了;
首先来看一下目前数据库中有哪些表,键入 .table 命令;
可以看到,此时数据库中有两张表:
- android_metadata表 是每个数据库中都会自动生成的,不用管它
- 而另外一张 Book表 就是我们在 MyDatabaseHelper 中创建的了
这里还可以通过 .schema 命令来查看它们的建表语句;
由此证明,BookStore.db数据库 和 Book表 确实已经创建成功了;
之后键人 .exit 或 .quit 命令可以退出数据库的编辑,再键入 exit 命令就可以退出设备控制台了。
通过 SQLite Expert 调试
当然,除了使用 adb 工具外,还可以通过使用 SQLite 可视化管理工具来查看;
SQLite Expert 是一款功能强大的 SQLite 可视化管理工具,SQLite Expert 允许用户在 SQLite 服务器上执行创建、编辑、复制、提取等操作。
可以帮我们查看 SQLite 数据库以及生成 SQLite 数据库;
【SQLite Expert 下载地址】,我下载的是这个版本【SQLite Expert Personal 64bit】;
通过 Android Device Monitor 找到 BookStore.db 文件,导出到本地(选中 BookStore.db 后选择右上角的导出按钮保存到本地);
并通过 SQLite Expert 打开该文件;
相比于使用 adb 调试,真的是简单粗暴,接下来就通过 SQLite Expert 调试了;
升级数据库
如果你足够细心,一定会发现 MyDatabaseHelper中还有一个空方法呢;
没错, onUpgrade() 方法是用于对数据库进行升级的,它在整个数据库的管理工作当中起着非常重要的作用,可千万不能忽视。
目前 DatabaseTest项目 中已经有一张 Book表 用于存放书的各种详细数据;
如果我们想再添加一张Category表用于记录图书的分类,该怎么做呢?
比如 Category表中有 id(主键)、分类名和分类代码 这几个列,那么建表语句就可以写成:
CREATE TABLE Category( id INTEGER PRIMARY KEY , category_name TEXT, category_code INTEGER );接下来将这条建表语句添加到 MyDatabaseHelper 中,修改 MyDatabaseHelper.java 中的代码;
MyDatabase.java
public class MyDatabaseHelper extends SQLiteOpenHelper { private Context mContext; public MyDatabaseHelper(...) {...} @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); db.execSQL("CREATE TABLE Category(" + "id INTEGER PRIMARY KEY ," + "category_name TEXT," + "category_code INTEGER)"); Toast.makeText(mContext,"Create Succeeded!",Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(...) {...} }看上去好像都挺对的吧?
现在我们重新运行一下程序,并点击 Create按钮,你会发现,没有弹出创建成功的提示。
当然,你也可以通过 SQLite Expert工具 到数据库中再去检查一下,这样你会更加地确认 Category表 没有创建成功。
其实没有创建成功的原因不难思考,因为此时 BookStore.db数据库 已经存在了,
之后不管我们怎样点击 Create按钮,MyDatabaseHelper 中的 onCreate() 方法都不会再次执行,因此新添加的表也就无法得到创建了。
解决这个问题的办法也相当简单,只需要先将程序卸载掉,然后重新运行,这时 BookStore.db数据库 已经不存在了;
如果再点击 Create按钮,MyDatabaseHelper 中的 onCreate() 方法就会执行,这时 Category表 就可以创建成功了。
不过,通过卸载程序的方式来新增一张表毫无疑问是很极端的做法;
其实我们只需要巧妙地运用 SQLiteOpenHelper 的升级功能就可以很轻松地解决这个问题;
修改 MyDatabaseHelper.java 中的代码;
MyDatabaseHelper.java
public class MyDatabaseHelper extends SQLiteOpenHelper { ... public MyDatabaseHelper(...) {...} @Override public void onCreate(SQLiteDatabase db) {...} @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() 方法中执行了两条DROP语句;
如果发现数据库中已经存在 Book表 或 Category表 了,就将这两张表删除掉,然后再调用 onCreate() 方法重新创建;
这里先将已经存在的表删除掉,因为如果在创建表时发现这张表已经存在了,就会直接报错。
接下来的问题就是如何让 onUpgrade() 方法能够执行了,还记得SQLiteOpenHelper的构造方法里接收的第四个参数吗?
它表示当前数据库的版本号,之前我们传入的是 1,现在只要传入一个与 1 不同的整数,就可以让 onUpgrade() 方法得到执行了。
修改 MainActivity.java 中的代码;
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); helper = new MyDatabaseHelper(this,"BookStore.db",null,2); ... } @Override public void onClick(View v) {...} }这里将数据库版本号指定为2,表示我们对数据库进行升级了。
现在重新运行程序,并点击Create按钮,这时就会再次弹出创建成功的提示;
为了验证一下 Category表 是不是已经创建成功了,我们在 SQLite Expert 中打开 BookStore.db 数据库;
(注意,每次在更新 BookStore.db 后,都需要重新导出一遍)
由此可以看出,Category表 已经创建成功了,同时也说明我们的升级功能的确起到了作用。
•CRUD
我们可以对数据进行的操作有 4 种,即 CRUD:
- C 代表添加(Create)
- R 代表查询(Retrieve)
- U 代表更新(Update)
- D 代表删除(Delete)
每一种操作又各自对应了一种 SQL 命令,如果你比较熟悉SQL语言的话,一定会知道:
- 添加数据时使用 insert
- 查询数据时使用 select
- 更新数据时使用 update
- 删除数据时使用 delete
但是开发者的水平总会是参差不齐的,未必每一个人都能非常熟悉地使用SQL语言;
因此Android也提供了一系列的辅助性方法,使得在 Android 中即使不去编写 SQL 语句,也能轻松完成所有的 CRUD 操作。
前面我们已经知道,调用 SQLiteOpenHelper 的 getReadableDatabase() 或 getWritableDatabase() 方法是可以用于创建和升级数据库的;
不仅如此,这两个方法还都会返回一个 SQLiteDatabase 对象,借助这个对象就可以对数据进行 CRUD 操作了。
添加数据
SQLiteDatabase 中提供了一个 insert() 方法,这个方法就是专门用于添加数据的,它接收3个参数:
- 第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字
- 第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可
- 第三个参数是一个 ContentValues 对象,它提供了一系列的 put() 方法重载,用于向 ContentValues 中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可
介绍完了基本用法,接下来还是让我们通过例子的方式来亲身体验一下如何添加数据吧;
修改 activity_main.xml 中的代码,如下所示;
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" android:padding="10dp"> <Button android:id="@+id/create" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Create" android:textAllCaps="false" /> <Button android:id="@+id/add" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Add" android:textAllCaps="false" /> </LinearLayout>可以看到,我们在布局文件中又新增了一个按钮,稍后就会在这个按钮的点击事件里编写添加数据的逻辑。
接着修改 MainActivity.java 中的代码;
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ ... private Button mBtnAdd; @Override protected void onCreate(Bundle savedInstanceState) { ... mBtnAdd = findViewById(R.id.add); mBtnAdd.setOnClickListener(this); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.create: helper.getWritableDatabase(); break; case R.id.add: SQLiteDatabase db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); //开始组装第一条数据 values.put("name","《第一行代码》"); values.put("price",60.3); db.insert("Book",null,values); values.clear(); //开始组装第二条数据 values.put("name","《SQL必知必会》"); values.put("price",16.99); db.insert("Book",null,values); values.clear(); Toast.makeText(this,"Add Succeeded!",Toast.LENGTH_SHORT).show(); break; } } }在添加数据按钮的点击事件里面,我们先获取到了 SQLiteDatabase 对象,然后使用 ContentValues 来对要添加的数据进行组装。
如果你比较细心的话应该会发现,这里只对 Book表 里其中两列的数据进行了组装,id 那一列没并没给它赋值;
这是因为在前面创建表的时候,我们就将 id列 设置为自增长了,它的值会在入库的时候自动生成,所以不需要手动给它赋值了。
接下来调用了 insert() 方法将数据添加到表当中;
注意这里我们实际上添加了两条数据,上述代码中使用 Contentvalues 分别组装了两次不同的内容,并调用了两次 insert() 方法。
运行结果
通过 SQLite Expert 打开 BookStore.db数据库 来查看一下(先导出);
由此可以看出,我们刚刚组装的两条数据都已经准确无误地添加到Book表中了。
更新数据
学习完了如何向表中添加数据,接下来我们看看怎样才能修改表中已有的数据。
SQLiteDatabase 中也提供了一个非常好用的 update() 方法,用于对数据进行更新,这个方法接收四个参数:
- 第一个参数和 insert() 方法一样,也是表名,在这里指定去更新哪张表里的数据
- 第二个参数是 ContentValues 对象,把要更新的数据在这里组装进去
- 第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行
那么接下来我们仍然是在 TestDatabase 项目的基础上修改,看一下更新数据的具体用法。
比如说刚才添加到数据库里的第一本书,由于过了畅销季,卖得不是很火了,现在需要通过降低价格的方式来吸引更多的顾客,我们应该怎么操作呢?
首先修改 activity_main.xml 中的代码;
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" android:padding="10dp"> <Button.../> <Button.../> <Button android:id="@+id/update" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="update" android:textAllCaps="false" /></LinearLayout>接下来修改 MainActivity.java 中的代码;
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private MyDatabaseHelper helper; private SQLiteDatabase db; private ContentValues values; private Button mBtnCreate; private Button mBtnAdd; private Button mBtnUpdate; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); helper = new MyDatabaseHelper(this,"BookStore.db",null,2); values = new ContentValues(); mBtnCreate = findViewById(R.id.create); mBtnCreate.setOnClickListener(this); mBtnAdd = findViewById(R.id.add); mBtnAdd.setOnClickListener(this); mBtnUpdate = findViewById(R.id.update); mBtnUpdate.setOnClickListener(this); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.create: helper.getWritableDatabase(); break; case R.id.add: db = helper.getWritableDatabase(); values.clear(); values.put("name","《第一行代码》"); values.put("price",60.3); db.insert("Book",null,values); values.clear(); values.put("name","《SQL必知必会》"); values.put("price",16.99); db.insert("Book",null,values); Toast.makeText(this,"Add Succeeded!",Toast.LENGTH_SHORT).show(); break; case R.id.update: db = helper.getWritableDatabase(); values.clear(); values.put("price",42.5); db.update("Book",values,"name = ?",new String[]{"《第一行代码》"}); Toast.makeText(this,"Update Succeeded!",Toast.LENGTH_SHORT).show(); break; } } }这里在更新数据按钮的点击事件里面构建了一个 ContentValues 对象,并且只给它指定了一组数据,说明我们只是想把价格这一列的数据更新成 42.5;
然后调用了 SQLiteDatabase 的 update() 方法去执行具体的更新操作,可以看到,这里使用了 第三、第四个参数 来指定具体更新哪几行;
第三个参数对应的是 SQL 语句的 where 部分,表示更新所有 name 等于 ? 的行;
而 ? 是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。
因此上述代码想表达的意图是将名字是《第一行代码》的这本书的价格改成 42.5;
需要注意的是,相较于上一次的代码,这次我将 private SQLiteDatabase db; 和 private ContentValues values; 提取了出来;
因为在 case 中第二次设置 db 和 values 的时候报错,报错原因是存在 case 中包含这两个变量的设置;
运行结果
导出后查询表中的数据情况;
可以看到,《第一行代码》这本书的价格已经被成功改为 42.5 了。
删除数据
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" android:padding="10dp"> <Button.../> <Button.../> <Button.../> <Button android:id="@+id/delete" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Delete" android:textAllCaps="false" /> </LinearLayout>MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ ... private Button mBtnDelete; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... mBtnDelete = findViewById(R.id.delete); mBtnDelete.setOnClickListener(this); } @Override public void onClick(View v) { switch(v.getId()){ ... case R.id.delete: db = helper.getWritableDatabase(); db.delete("Book","id = ?",new String[]{"2"}); Toast.makeText(this,"Delete Succeeded!",Toast.LENGTH_SHORT).show(); break; } } }可以看到,我们在删除按钮的点击事件里指明去删除 Book表 中的数据,
并且通过第二、第三个参数来指定删除第二本书。
运行结果
导出后查询表中的数据情况;
查询数据
SQLiteDatabase 中提供了一个 query() 方法用于对数据进行查询。
这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数。
那我们就先来看一下这七个参数各自的含义:
- 第一个参数不用说,当然还是表名,表示我们希望从哪张表中查询数据
- 第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列
- 第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据
- 第五个参数用于指定需要去 group by 的列,不指定则表示不对查询结果进行 group by 操作
- 第六个参数用于对 group by 之后的数据进行进一步的过滤,不指定则表示不进行过滤
- 第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式
更多详细的内容可以参考下表;
调用 query() 方法后会返回一个 Cursor 对象,查询到的所有数据都将从这个对象中取出。
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" android:padding="10dp"> <Button.../> <Button.../> <Button.../> <Button.../> <Button android:id="@+id/query" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Query" android:textAllCaps="false" /> </LinearLayout>MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private static final String TAG = "MainActivity"; ... private Button mBtnQuery; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... mBtnQuery = findViewById(R.id.query); mBtnQuery.setOnClickListener(this); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.create: ... break; case R.id.add: ... break; case R.id.update: ... break; case R.id.delete: ... break; case R.id.query: db = helper.getWritableDatabase(); //查询 Book 表中所有的数据 Cursor cursor = db.query("Book",null,null,null,null,null,null); if(cursor.moveToFirst()){ do{ //遍历 cursor 对象,取出数据并打印 String name = cursor.getString(cursor.getColumnIndex("name")); double price = cursor.getDouble(cursor.getColumnIndex("price")); Log.d(TAG, "************"); Log.d(TAG, "book name is "+name); Log.d(TAG, "book price is "+price); }while(cursor.moveToNext()); cursor.close(); } break; } } }可以看到,我们首先在查询按钮的点击事件里面调用了 SQLiteDatabase 的 query() 方法去查询数据;
这里的 query() 方法非常简单,只是使用了第一个参数指明去查询 Book表,后面的参数全部为 null,这就表示希望查询这张表中的所有数据,虽然这张表中目前只剩下一条数据了;
查询完之后就得到了一个 Cursor对象;
接着我们调用它的 moveToFirst() 方法将数据的指针移动到第一行的位置,然后进入了一个循环当中,去遍历查询到的每一行数据;
在这个循环中可以通过 Cursor 的 getColumnIndex() 方法获取到某一列在表中对应的位置索引,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了;
接着我们使用Log的方式将驭出的数据打印出来,借此来检查一下读取工作有没有成功完成。
最后别忘了调用 close() 方法来关闭 cursor。
运行结果
可以看到,这里已经将 Book表 中唯一的一条数据成功地读取出来了。
•GitHub
【🔗】