SQLite数据库存储
SQLite 是一款轻量级的关系型数据库, 它的运算速度非常快,占用资源很少, 通常只需要几百 K 的内存就足够了, 因而特别适合在移动设备上使用。 SQLite不仅支持标准的 SQL 语法,还遵循了数据库的 ACID 事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手 SQLite。而 SQLite 又比一般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。 Android 正是把这个功能极为强大的数据库嵌入到了系统当中,使得本地持久化的功能有了一次质的飞跃。
创建数据库
Android 为了让我们能够更加方便地管理数据库, 专门提供了一个 SQLiteOpenHelper 帮助类, 借助这个类就可以非常简单地对数据库进行创建和升级。
首先你要知道 SQLiteOpenHelper 是一个抽象类,这意味着如果我们想要使用它的话,就需要创建一个自己的帮助类去继承它。 SQLiteOpenHelper 中有两个抽象方法,分别是onCreate()和 onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。
SQLiteOpenHelper 中 还 有 两 个 非 常 重 要 的 实 例 方 法 , getReadableDatabase() 和getWritableDatabase()。 这两个方法都以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,getWritableDatabase()方法则将出现异常。
SQLiteOpenHelper 中有两个构造方法可供重写, 一般使用参数少一点的那个构造方法即可。这个构造方法中接收四个参数,第一个参数是 Context,这个没什么好说的,必须要有它才能对数据库进行操作。 第二个参数是数据库名, 创建数据库时使用的就是这里指定的名称。第三个参数允许我们在查询数据的时候返回一个自定义的 Cursor,一般都是传入 null。第 四 个 参 数 表 示 当 前 数 据 库 的 版 本 号 , 可 用 于 对 数 据 库 进 行 升 级 操 作 。 构 建 出SQLiteOpenHelper 的实例之后, 再调用它的 getReadableDatabase()或 getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data/packagename/databases/目录下。此时,重写的 onCreate()方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。
通过例子的方式来更加直观地体会 SQLiteOpenHelper 的用法,首先新建一个 DatabaseTest 项目。
这里创建一个名为 BookStore.db 的数据库, 然后在这个数据库中新建一张 book表,表中有 id(主键)、作者、价格、页数和书名等列。
public class MyDataBaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table book(id integer primary key autoincrement,author text,price real,pages integer,name text)";
private Context mContext;
public MyDataBaseHelper(Context context, String name, 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 successed", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
把建表语句定义成了一个字符串常量,然后在 onCreate()方法中又调用了 SQLiteDatabase 的 execSQL()方法去执行这条建表语句, 并弹出一个 Toast 提示创建成功,这样就可以保证在数据库创建完成的同时还能成功创建 book 表。
现在修改 activity_main.xml 中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"
android:textAllCaps="false" />
</LinearLayout>
布局文件很简单, 就是加入了一个按钮, 用于创建数据库。 最后修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private MyDataBaseHelper dataBaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataBaseHelper = new MyDataBaseHelper(this, "BookStore.db", null, 1);
Button btn_create = (Button) findViewById(R.id.create);
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dataBaseHelper.getWritableDatabase();
}
});
}
}
在 onCreate()方法中构建了一个 MyDatabaseHelper 对象, 并且通过构造函数的参数将数据库名指定为 BookStore.db,版本号指定为 1,然后在 Create database 按钮的点击事件里调用了getWritableDatabase()方法。 这样当第一次点击 Create database 钮时, 就会检测到当前程序中并没有 BookStore.db 这个数据库, 于是会创建该数据库并调用MyDatabaseHelper中的 onCreate()方法,这样 book 表也就得到了创建,然后会弹出一个 Toast 提示创建成功。再次点击 Create database 按钮时,会发现此时已经存在 BookStore.db 数据库了,因此不会再
创建一次。现在就可以运行一下代码了, 在程序主界面点击 Create database 按钮, 结果如图所示。
此时 BookStore.db 数据库和 book 表应该都已经创建成功了,因为当你再次点击 Create database 按钮时不会再有 Toast 弹出。可是又回到了之前的那个老问题,怎样才能证实它们的确是创建成功了?如果还是使用 File Explorer, 那么最多你只能看到 databases 目录下出现了一个 BookStore.db 文件, book 表是无法通过 File Explorer 看到的。 因此这次我们准备换一种查看方式,使用 adb shell 来对数据库和表的创建情况进行检查。
adb 是 Android SDK 中自带的一个调试工具,使用这个工具可以直接对连接在电脑上的手机或模拟器进行调试操作。 它存放在 sdk 的platform-tools 目录下, 如果想要在命令行中使用这个工具,就需要先把它的路径配置到环境变量里。
如果你使用的是 Windows 系统,可以右击我的电脑→属性→高级→环境变量,然后在系统变量里找到 Path 并点击编辑,将 platform-tools 目录配置进去。
配置好了环境变量之后,就可以使用 adb 工具了。打开命令行界面,输入 adb shell,就会进入到设备的控制台,然后使用 cd 命令进行到/data/data/com.example.shijj.databasetest/databases/目录下,并使用 ls命令查看到该目录里的文件:
这个目录下出现了两个数据库文件,一个正是我们创建的 BookStore.db,而另一个BookStore.db-journal 则是为了让数据库能够支持事务而产生的临时日志文件, 通常情况下这个文件的大小都是 0 字节。接下来我们就要借助 sqlite 命令来打开数据库了,只需要键入 sqlite3,后面加上数据库名即可,如图所示:
可以看到,此时数据库中有两张表, android_metadata 表是每个数据库中都会自动生成的, 不用管它, 而另外一张 book 表就是我们在 MyDatabaseHelper 中创建的了。 这里还可以通过.schema 命令来查看它们的建表语句,如图所示:
由此证明, BookStore.db 数据库和 book 表确实已经是创建成功了。 之后键入.exit 或.quit命令可以退出数据库的编辑,再键入 exit 命令就可以退出设备控制台了。
升级数据库
MyDatabaseHelper中还有一个 onUpgrade()方法是用于对数据库进行升级的, 它在整个数据库的管理工作当中起着非常重要的作用, 可
千万不能忽视它。目前 DatabaseTest 项目中已经有一张 book 表用于存放书的各种详细数据,我们再添加一张表category表,写到MyDatabaseHelper类中:
public class MyDataBaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table book(id integer primary key autoincrement,author text,price real,pages integer,name text)";
public static final String CREATE_CATEGORY = "create table category(id integer primary key autoincrement,category_name text,category_code integer)";
private Context mContext;
public MyDataBaseHelper(Context context, String name, 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 successed", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
现在我们重新运行一下程序, 并点击 Create database 按钮, 卧槽,
竟然没有弹出创建成功的提示。 当然, 你也可以通过 adb 工具到数据库中再去检查一下, 这样你会更加地确认, category 表没有创建成功!
其实没有创建成功的原因不难思考, 因为此时 BookStore.db 数据库已经存在了, 之后不管我们怎样点击 Create database 按钮, MyDatabaseHelper 中的 onCreate()方法都不会再次执行,因此新添加的表也就无法得到创建了。
解决这个问题的办法也相当简单,只需要先将程序卸载掉,然后重新运行,这时BookStore.db 数据库已经不存在了,如果再点击 Create database 按钮, MyDatabaseHelper 中的 onCreate()方法就会执行,这时 category 表就可以创建成功了。
不过通过卸载程序的方式来新增一张表毫无疑问是很极端的做法, 其实我们只需要巧妙地运用 SQLiteOpenHelper 的升级功能就可以很轻松地解决这个问题。修改 MyDatabaseHelper中的代码,如下所示:
@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 中的代码,如下所示:
dataBaseHelper = new MyDataBaseHelper(this, "BookStore.db", null, 2);
Button btn_create = (Button) findViewById(R.id.create);
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dataBaseHelper.getWritableDatabase();
}
});
}
这里将数据库版本号指定为 2, 表示我们对数据库进行升级了。 现在重新运行程序, 并点击 Create database 按钮, 这时就会再次弹出创建成功的提示。 为了验证一下 category 表是不是已经创建成功了,我们在 adb shell 中打开 BookStore.db 数据库,然后键入.table 命令,结果如图所示:
接着键入.schema 命令查看一下建表语句,结果如图所示:
由此可以看出, category 表已经创建成功了,同时也说明我们的升级功能的确起到了作用。
添加数据
现在已经掌握了创建和升级数据库的方法, 接下来就该学习一下如何对表中的数据进行操作了。其实我们可以对数据进行的操作也就无非四种,即 CRUD。其中 C 代表添加(Create), R 代表查询(Retrieve), U 代表更新(Update), D 代表删除(Delete)。 每一种操作又各自对应了一种 SQL 命令,如果你比较熟悉 SQL 语言的话,一定会知道添加数据时使用 insert,查询数据时使用 select,更新数据时使用 update,删除数据时使用 delete。但是开发者的水平总会是参差不齐的, 未必每一个人都能非常熟悉地使用 SQL 语言, 因此 Android也是提供了一系列的辅助性方法, 使得在 Android 中即使不去编写 SQL 语句, 也能轻松完成所有的 CRUD 操作。
前面我们已经知道, 调用 SQLiteOpenHelperde的getReadableDatabase()或 getWritableDatabase()方法是可以用于创建和升级数据库的, 不仅如此, 这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行 CRUD 操作了。
那么我们一个一个功能地看,首先学习一下如何向数据库的表中添加数据吧。SQLiteDatabase 中提供了一个 insert()方法, 这个方法就是专门用于添加数据的。 它接收三个参数, 第一个参数是表名, 我们希望向哪张表里添加数据, 这里就传入该表的名字。 第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值 NULL, 一般我们用不到这
个功能, 直接传入 null 即可。 第三个参数是一个 ContentValues 对象, 它提供了一系列的 put()方法重载,用于向 ContentValues 中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
接下来还是让我们通过例子的方式来亲身体验一下如何添加数据吧。修改 activity_main.xml 中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"
android:textAllCaps="false" />
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add data"
android:textAllCaps="false" />
</LineraLayout>
可以看到, 我们在布局文件中又新增了一个按钮, 稍后就会在这个按钮的点击事件里编写添加数据的逻辑。接着修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private MyDataBaseHelper dataBaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataBaseHelper = new MyDataBaseHelper(this, "BookStore.db", null, 2);
Button btn_create = (Button) findViewById(R.id.create);
Button btn_add = (Button) findViewById(R.id.add);
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dataBaseHelper.getWritableDatabase();
}
});
btn_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("book", null, values);
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("book", null, values);
}
});
}
}
在添加数据按钮的点击事件里面,先获取到了 SQLiteDatabase 对象,然后使用ContentValues 来对要添加的数据进行组装。 接下来调用了 insert()方法将数据添加到表当中, 注意这里我们实际上添加了两条数据,上述代码中使用 ContentValues 分别组装了两次不同的内容,并调用了两次 insert()方法。好了,现在可以重新运行一下程序了,如图所示:
点击一下 Add data 按钮, 此时两条数据应该都已经添加成功了, 不过为了证实一下, 我们还是打开 BookStore.db 数据库瞧一瞧。输入 SQL 查询语句 select * from book,结果如图所示:
由此可以看出,我们刚刚组装的两条数据,都已经准确无误地添加到 book 表中了。
更新数据
看完如何向表中添加数据,接下来看看怎样才能修改表中已有的数据。SQLiteDatabase 中也是提供了一个非常好用的 update()方法用于对数据进行更新,这个方法接收四个参数, 第一个参数和 insert()方法一样, 也是表名, 在这里指定去更新哪张表里的数据。第二个参数是 ContentValues 对象,要把更新数据在这里组装进去。第三、第四个参数用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。那么接下来我们仍然是在 DatabaseTest 项目的基础上修改, 看一下更新数据的具体用法。首先修改 activity_main.xml 中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"
android:textAllCaps="false" />
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add data"
android:textAllCaps="false" />
<Button
android:id="@+id/update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update data"
android:textAllCaps="false" />
</LinearLayout>
布局文件中的代码就已经非常简单了, 就是添加了一个用于更新数据的按钮。 然后修改MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private MyDataBaseHelper dataBaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataBaseHelper = new MyDataBaseHelper(this, "BookStore.db", null, 2);
Button btn_create = (Button) findViewById(R.id.create);
Button btn_add = (Button) findViewById(R.id.add);
Button btn_update = (Button) findViewById(R.id.update);
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dataBaseHelper.getWritableDatabase();
}
});
btn_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("book", null, values);
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("book", null, values);
}
});
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("book", values, "name=?", new String[]{"The Da Vinci Code"});
}
});
}
}
这里在更新数据按钮的点击事件里面构建了一个 ContentValues 对象,并且只给它指定了一组数据, 说明我们只是想把价格这一列的数据更新成 10.99。 然后调用了 SQLiteDatabase的 update()方法去执行具体的更新操作,可以看到,这里使用了第三、第四个参数来指定具体更新哪几行。第三个参数对应的是 SQL 语句的 where 部分,表示去更新所有 name 等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。因此上述代码想表达的意图就是,将名字是 The Da Vinci Code的这本书的价格改成 10.99。
现在重新运行一下程序,如图所示:
点击一下 Update data 按钮后,再次输入查询语句查看表中的数据情况,结果如图所示:
可以看到, The Da Vinci Code 这本书的价格已经被成功改为 10.99 了.
删除数据
删除数据就更简单了,SQLiteDatabase 中提供了一个 delete()方法专门用于删除数据, 这个方法接收三个参数, 第一个参数仍然是表名, 这个已经没什么好说的了, 第二、 第三个参数又是用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
继续动手实践, 修改 activity_main.xml 中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"
android:textAllCaps="false" />
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add data"
android:textAllCaps="false" />
<Button
android:id="@+id/update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update data"
android:textAllCaps="false" />
<Button
android:id="@+id/delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data"
android:textAllCaps="false" />
</LinearLayout>
仍然是在布局文件中添加了一个按钮, 用于删除数据。 然后修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private MyDataBaseHelper dataBaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataBaseHelper = new MyDataBaseHelper(this, "BookStore.db", null, 2);
Button btn_create = (Button) findViewById(R.id.create);
Button btn_add = (Button) findViewById(R.id.add);
Button btn_update = (Button) findViewById(R.id.update);
Button btn_delete = (Button) findViewById(R.id.delete);
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dataBaseHelper.getWritableDatabase();
}
});
btn_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("book", null, values);
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("book", null, values);
}
});
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("book", values, "name=?", new String[]{"The Da Vinci Code"});
}
});
btn_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
db.delete("book", "pages>?", new String[]{"500"});
}
});
}
}
可以看到, 我们在删除按钮的点击事件里指明去删除 book 表中的数据, 并且通过第二、第三个参数来指定仅删除那些页数超过 500 页的书籍。 当然这个需求很奇怪, 这里也仅仅是为了做个测试。 你可以先查看一下当前 book 表里的数据, 其中 The Lost Symbol 这本书的页数超过了 500 页,也就是说当我们点击删除按钮时,这条记录应该会被删除掉。
现在重新运行一下程序,如图所示:
点击一下 Delete data 按钮后,再次输入查询语句查看表中的数据情况,结果如图所示:
这样就可以明显地看出, The Lost Symbol 这本书的数据已经被删除了。
查询数据
SQLiteDatabase 中还提供了一个 query()方法用于对数据进行查询。这个方法的参数非常复杂, 最短的一个方法重载也需要传入七个参数。 那我们就先来看一下这七个参数各自的含义吧, 第一个参数不用说, 当然还是表名, 表示我们希望从哪张表中查询数据。 第二个参数用于指定去查询哪几列, 如果不指定则默认查询所有列。 第三、 第四个参数用于去约束查询某一行或某几行的数据, 不指定则默认是查询所有行的数据。 第五个参数用于指定需要去 group by 的列, 不指定则表示不对查询结果进行 group by 操作。 第六个参数用于对 group by 之后的数据进行进一步的过滤, 不指定则表示不进行过滤。 第七个参数用于指定查询结果的排序方式, 不指定则表示使用默认的排序方式。我们不必为每条查询语句都指定上所有的参数, 多数情况下只需要传入少数几个参数就可以完成查询操作了。 调用query()方法后会返回一个 Cursor 对象,查询到的所有数据都将从这个对象中取出。
下面还是让我们通过例子的方式来体验一下查询数据的具体用法,修改 activity_main.xml中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"
android:textAllCaps="false" />
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add data"
android:textAllCaps="false" />
<Button
android:id="@+id/update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update data"
android:textAllCaps="false" />
<Button
android:id="@+id/delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data"
android:textAllCaps="false" />
<Button
android:id="@+id/query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query data"
android:textAllCaps="false" />
</LinearLayout>
这个已经没什么好说的了, 添加了一个按钮用于查询数据。 然后修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private MyDataBaseHelper dataBaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataBaseHelper = new MyDataBaseHelper(this, "BookStore.db", null, 2);
Button btn_create = (Button) findViewById(R.id.create);
Button btn_add = (Button) findViewById(R.id.add);
Button btn_update = (Button) findViewById(R.id.update);
Button btn_delete = (Button) findViewById(R.id.delete);
Button btn_query = (Button) findViewById(R.id.query);
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dataBaseHelper.getWritableDatabase();
}
});
btn_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("book", null, values);
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("book", null, values);
}
});
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("book", values, "name=?", new String[]{"The Da Vinci Code"});
}
});
btn_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
db.delete("book", "pages>?", new String[]{"500"});
}
});
btn_query.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
Cursor cursor = db.query("book", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d("sjj---->", "book name is " + name);
Log.d("sjj---->", "book author is " + author);
Log.d("sjj---->", "book price is " + price);
Log.d("sjj---->", "book pages are " + pages);
} while (cursor.moveToNext());
}
cursor.close();
}
});
}
}
可以看到,我们首先在查询按钮的点击事件里面调用了 SQLiteDatabase 的 query()方法去查询数据。 这里的 query()方法非常简单, 只是使用了第一个参数指明去查询 book 表, 后面的参数全部为 null。 这就表示希望查询这张表中的所有数据, 虽然这张表中目前只剩下一条数据了。 查询完之后就得到了一个 Cursor 对象, 接着我们调用它的 moveToFirst()方法将数据的指针移动到第一行的位置, 然后进入了一个循环当中, 去遍历查询到的每一行数据。 在这个循环中可以通过 Cursor 的 getColumnIndex()方法获取到某一列在表中对应的位置索引 ,然后将这个索引传入到相应的取值方法中, 就可以得到从数据库中读取到的数据了。 接着我们使用 Log 的方式将取出的数据打印出来, 借此来检查一下读取工作有没有成功完成。 最后别忘了调用 close()方法来关闭 Cursor。
好了,现在再次重新运行程序,如图所示:
点击一下 Query data 按钮后,查看 LogCat 的打印内容,结果如图所示:
可以看到,这里已经将 book 表中唯一的一条数据成功地读取出来了。
使用SQL操作数据库
虽然 Android 已经给我们提供了很多非常方便的 API 用于操作数据库, 不过总会有一些人不习惯去使用这些辅助性的方法,而是更加青睐于直接使用 SQL 来操作数据库。这种人一般都是属于 SQL 大牛, 如果你也是其中之一的话, 那么恭喜, Android 充分考虑到了你们的编程习惯,同样提供了一系列的方法,使得可以直接通过 SQL 来操作数据库。
下面我就来简略演示一下, 如何直接使用 SQL 来完成前面的 CRUD 操作。
添加数据的方法如下:
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.rawQuery("select * from book", null);
可以看到, 除了查询数据的时候调用的是 SQLiteDatabase 的 rawQuery()方法, 其他的操作都是调用的 execSQL()方法。以上演示的几种方式,执行结果会和前面的 CRUD 操作的结果完全相同,选择使用哪一种方式就看你个人的喜好了。