Android学习笔记(十七)——数据库操作(下)
//此系列博文是《第一行Android代码》的学习笔记,如有错漏,欢迎指正!
这一次我们来试一试升级数据库,并进行数据库的CRUD操作,其中, C 代表添加(Create) ,R 代表查询(Retrieve) ,U代表更新(Update) ,D代表删除(Delete) 。每一种操作各自对应着一种 SQL命令。前面我们已经知道,调用 SQLiteOpenHelper的 getReadableDatabase()或 getWritableDatabase()方法是可以用于创建和升级数据库的, 不仅如此, 这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行 CRUD 操作了。
一、升级数据库:
假如我们已经有了一个BookStores用于储存书籍,现在我们想再建一个Category用于储存书籍的分类,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MyDatabaseHelper extends SQLiteOpenHelper {
2 public static final String CREATE_BOOK = "create table Book ("
3 + "id integer primary key autoincrement, "
4 + "author text, "
5 + "price real, "
6 + "pages integer, "
7 + "name text)";
8 public static final String CREATE_CATEGORY = "create table Category ("
9 + "id integer primary key autoincrement, "
10 + "category_name text, "
11 + "category_code integer)";
12 private Context mContext;
13 public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
14 super(context, name, factory, version);
15 mContext = context;
16 }
17 @Override
18 public void onCreate(SQLiteDatabase db) {
19 db.execSQL(CREATE_BOOK);
20 db.execSQL(CREATE_CATEGORY);
21 Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).
22 show();
23 }
24 @Override
25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
26 }
27 }
当我们启动程序时,会发现Category并没有被建立。因为此时 BookStore.db 数据库已经存在了,之后不管我们怎样点击 Create database 按钮,MyDatabaseHelper 中的 onCreate()方法都不会再次执行,因此新添加的表也就无法得到创建了。
我们有一个简单而粗暴的方法来解决这个问题:只需要先将程序卸载掉,然后重新运行。这时BookStore.db 数据库已经不存在了,如果再点击 Create database 按钮,MyDatabaseHelper 中的 onCreate()方法就会执行,这时 Category表就可以创建成功了。这种方法显然过于极端,所以我们选择使用onUpdate()方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @Override
2 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
3 db.execSQL("drop table if exists Book");
4 db.execSQL("drop table if exists Category");
5 onCreate(db);
6 }
onUpgrade()方法中执行了两条 DROP 语句,如果发现数据库中已经存在 Book 表或 Category 表了,就将这两张表删除掉,然后再调用 onCreate()方法去重新创建。这里先将已经存在的表删除掉,是因为如果在创建表时发现这张表已经存在了,就会直接报错。
为了让onUpgrade()方法能够执行,我们得使用SQLiteOpenHelper 的构造方法里接收的第四个参数,它表示当前数据库的版本号,之前我们传入的是 1,现在只要传入一个比 1 大的数, 就可以让 onUpgrade()方法得到执行了。代码如下:
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
这样我们就能成功新建Category了。启动程序后按下按钮,然后在 adb shell中打开 BookStore.db 数据库,然后键入.table 命令,结果所下所示:
可以看到此时数据库中有三张表,android_metadata 表是每个数据库中都会自动生成的,不用管它,另外键入.schema 命令便可以查看一下建表语句。
二、添加数据:
SQLiteDatabase 中提供了一个 insert()方法,这个方法就是专门用于添加数据的。它接收三个参数,第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值 NULL, 一般我们用不到这个功能, 直接传入 null 即可。 第三个参数是一个 ContentValues 对象, 它提供了一系列的 put()方法重载,用于向 ContentValues 中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。现在我们就在BookStores中添加两本书,首先我们添加一个Button元素:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <Button
2 android:id="@+id/add_data"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:text="Add data"
6 />
然后在主活动中修改代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Button addData = (Button) findViewById(R.id.add_data);
2 addData.setOnClickListener(new View.OnClickListener() {
3 @Override
4 public void onClick(View v) {
5 SQLiteDatabase db = dbHelper.getWritableDatabase();
6 ContentValues values = new ContentValues();// 开始组装第一条数据
7 values.put("name", "Let's learn Android");
8 values.put("author", "Doctor ha");
9 values.put("pages", 454);
10 values.put("price", 16.96);
11 db.insert("Book", null, values); // 插入第一条数据
12 values.clear();// 开始组装第二条数据
13 values.put("name", "Why I give in learning Android");
14 values.put("author", "Doctor ha");
15 values.put("pages", 510);
16 values.put("price", 19.95);
17 db.insert("Book", null, values); // 插入第二条数据
18 }
19 });
在添加数据按钮的点击事件里面,我们先获取到了 SQLiteDatabase 对象,然后使用ContentValues来对要添加的数据进行组装。 如果你比较细心的话应该会发现, 这里只对 Book表里其中四列的数据进行了组装,id那一列没并没给它赋值。这是因为在前面创建表的时候我们就将 id 列设置为自增长了,它的值会在入库的时候自动生成,所以不需要手动给它赋值了。 接下来调用了 insert()方法将数据添加到表当中, 注意这里我们实际上添加了两条数据,上述代码中使用 ContentValues 分别组装了两次不同的内容,并调用了两次 insert()方法。
可以看到两本书都已经储存在数据库里了。(注意,SQL语句句末须加分号)
三、更新数据:
SQLiteDatabase 中也是提供了一个非常好用的 update()方法用于对数据进行更新,这个方法接收四个参数,第一个参数和 insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。第二个参数是 ContentValues 对象,要把更新数据在这里组装进去。第三、第四个参数用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。
接下来我们仍然是在Database项目的基础上修改, 体会一下更新数据的具体用法。比如说刚才添加到数据库里的第一本书,我们现在尝试更新它的价格,首先添加一个button:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <Button
2 android:id="@+id/update_data"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:text="Update data"
6 />
然后在主活动中添加下列代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Button updateData = (Button) findViewById(R.id.update_data);
2 updateData.setOnClickListener(new View.OnClickListener() {
3 @Override
4 public void onClick(View v) {
5 SQLiteDatabase db = dbHelper.getWritableDatabase();
6 ContentValues values = new ContentValues();
7 values.put("price", 10.99);
8 db.update("Book", values, "name = ?", new String[]{"Let's learn Android" });
9 }
10 });
这里在更新数据按钮的点击事件里面构建了一个 ContentValues 对象,并且只给它指定了一组数据, 说明我们只是想把价格这一列的数据更新成 10.99。 然后调用了 SQLiteDatabase的 update()方法去执行具体的更新操作,可以看到,这里使用了第三、第四个参数来指定具体更新哪几行。第三个参数对应的是 SQL 语句的 where 部分,表示去更新所有 name 等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。
运行之后效果如下:
可以看到第一本书的价格已被更新了。
四、删除数据:
SQLiteDatabase 中提供了一个 delete()方法专门用于删除数据,这个方法接收三个参数,第一个参数仍然是表名,这个已经没什么好说的了,第二、第三个参数又是用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。同样的,我们先声明一个button:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <Button
2 android:id="@+id/delete_data"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:text="Delete data"
6 />
接着在主活动中添加如下代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Button deleteButton = (Button) findViewById(R.id.delete_data);
2 deleteButton.setOnClickListener(new View.OnClickListener() {
3 @Override
4 public void onClick(View v) {
5 SQLiteDatabase db = dbHelper.getWritableDatabase();
6 db.delete("Book", "pages > ?", new String[]{"500" });
7 }
8 });
我们在删除按钮的点击事件里指明去删除 Book表中的数据, 并且通过第二、第三个参数来指定仅删除那些页数超过 500页的书籍。
运行效果如下:
可以看到第二本书的数据已被删除了。
五、查询数据:
SQL的全称是 Structured Query Language,它的大部分功能是用于查询,其他的功能只是小部分,所以查询数据会比较复杂。Android中使用query()方法来对数据进行查 询,这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数:
第一个参数是表名,表示我们希望从哪张表中查询数据;
第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列;
第三、第四个参数用于去约束查询某一行或某几行的数据,不指定则默认是查询所有行的数据;
第五个参数用于指定需要去 group by的列,不指定则表示不对查询结果进行 group by操作;
第六个参数用于对 group by之后的数据进行进一步的过滤,不指定则表示不进行过滤;
第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。
虽然 query()方法的参数非常多,但我们不必为每条查询语句都指定上所有的参数,多数情况下只需要传入少数几个参数就可以完成查询操作了。调用 query()方法后会返回一个 Cursor对象,查询到的所有数据都将从这个对象中取出。下面我们来实践一下,首先添加一个button:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
接着在主活动中添加代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Button queryButton = (Button) findViewById(R.id.query_data);
2 queryButton.setOnClickListener(new View.OnClickListener() {
3 @Override
4 public void onClick(View v) {
5 SQLiteDatabase db = dbHelper.getWritableDatabase();
6 // 查询Book 表中所有的数据
7 Cursor cursor = db.query("Book", null, null, null, null, null, null);
8 if (cursor.moveToFirst()) {
9 do {
10 // 遍历Cursor 对象,取出数据并打印
11 String name = cursor.getString(cursor.getColumnIndex("name"));
12 String author = cursor.getString(cursor.getColumnIndex("author"));
13 int pages = cursor.getInt(cursor.getColumnIndex("pages"));
14 double price = cursor.getDouble(cursor.getColumnIndex("price"));
15 Log.d("MainActivity", "book name is " + name);
16 Log.d("MainActivity", "book author is " + author);
17 Log.d("MainActivity", "book pages is " + pages);
18 Log.d("MainActivity", "book price is " + price);
19 } while (cursor.moveToNext());
20 }
21 cursor.close();
22 }
23 });
可以看到,我们首先在查询按钮的点击事件里面调用了 SQLiteDatabase 的 query()方法去查询数据。这里的 query()方法非常简单,只是使用了第一个参数指明去查询 Book 表,后面的参数全部为 null。这就表示希望查询这张表中的所有数据。 查询完之后就得到了一个 Cursor 对象, 接着我们调用它的 moveToFirst()方法将数据的指针移动到第一行的位置,然后进入了一个循环当中,去遍历查询到的每一行数据。在这个循环中可以通过 Cursor 的 getColumnIndex()方法获取到某一列在表中对应的位置索引,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。 接着我们使用 Log 的方式将取出的数据打印出来,借此来检查一下读取工作有没有成功完成。最后别忘了调用 close()方法来关闭 Cursor。
程序运行效果如下:
在Android Monitor中可以看到第一本书的所有数据。
//End.