android學習十一(android的SQLite資料庫的使用)
SQLite是一款輕量級的關係型資料庫,它運算速度快,佔用資源少,通常只需要幾百k的記憶體就夠了,支援標準的sql語法和資料庫的ACID事務。在android中為了能夠更加方便的管理資料庫,專門提供了一個SQLiteOpenHelper幫助類,藉助這個類就可以非常簡單的對資料庫進行建立和升級。
SQLiteOpenHelper是一個抽象類,如果我們要使用的話,就需要建立一個自己的類去繼承它。SQLiteOpenHelper中有2個抽象方法,分別是onCreate和onUpgrade方法,我們必須在自己的幫助類裡面重寫這兩個方法,然後分別在這兩個方法中實現建立,升級資料庫的邏輯。
SQLiteOpenHelper中還有2個非常重要的例項方法,getReadableDatabase()和getWritableDatabase()方法。這兩個方法都可以建立或開啟一個現有的資料庫(如果資料庫已經存在則直接開啟,否則建立一個新的資料庫),並返回一個可對資料庫進行讀寫操作的物件。不同的是當資料庫不可寫入的時候(如磁碟空間已滿)getReadableDatabase()方法返回的物件將以只讀的方式去開啟資料庫,而getWritableDatabase()方法方法將出現異常。
SQLiteOpenHelper中有2個構造方法可供重寫,一般使用引數少點的那個構造方法即可。這個構造方法中接收4個引數,第一個引數是Context,必須要有Context才能對資料庫進行操作。第二引數是資料庫名,建立資料庫時使用的就是這裡使用的名稱。第三個引數允許我們在查詢資料的時候返回一個自定義的Cursor,一般都是傳如null。第四個引數表示當前資料庫的版本號,可用於對資料庫進行升級操作。構建出SQLiteOpenHelper的例項後,在呼叫它的getReadableDatabase()或getWritableDatabase()方法就能夠建立資料庫了,資料庫檔案會存放在/data/data/<package
name>/databases/目錄下。此時,重寫的onCreate方法也會得到執行,所以通常在這裡去處理一些建立表的邏輯。
下面我們通過例子來學習SQLiteOpenHelper的用法吧。
建立資料庫
在android中integer表示整形,real表示浮點型,text表示文字型別,blob表示二進位制型別。
首先建立一個android專案,專案名為DatabaseTest。
新建MyDatabaseHelper類繼承SQLiteOpenHelper,程式碼如下所示:
package com.jack.databasetest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.widget.Toast; 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 context; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub this.context=context; } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(CREATE_BOOK); Toast.makeText(context, "create succeeded", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } }
上面的程式碼比較簡單,把建表語句定義成了一個字串常量,然後在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"
>
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="create database"
/>
</LinearLayout>
上面的佈局就只有一個按鈕,接下來我們在修改MainActivity中的程式碼:
package com.jack.databasetest;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得資料庫操作類物件
dbHelper=new MyDatabaseHelper(this, "BookStore.db",
null, 1);
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*
* 在onCreate方法中構建了一個MyDatabaseHelper物件,並且通過建構函式的引數
* 將資料庫名指定為BookStore.db,版本號為1,然後在create database按鈕的點選
* 事件裡呼叫了getWritableDatabase()方法,這樣當第一次點選create database按鈕時
* 就會檢測當前程式並沒有BookStore.db這個資料庫,於是會建立該資料庫並呼叫MyDatabaseHelper
* 中的onCreate()方法,這樣Book表也就得到了建立,然後會彈出一個Toast提示建立成功。再次點選
* create database按鈕時,會發現此時已經存在BookStore.db資料庫了,因此不會再建立一次了。
*
* */
dbHelper.getWritableDatabase();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
執行程式,點選按鈕的時候,出現如下結果:
當你在次點選按鈕的時候不在出現Toast提示了,因為資料庫已經存在了。這時我們用File Explor檢視,你會發現databases目錄下面出現了一個BookStore.db檔案,但是book表是無法通過File Exploer看到的。
由於不能通過File Exploer進行檢視所以我們這次藉助於adb shell來對資料庫和表的建立情況進行檢查。
adb是android sdk中自帶的一個除錯工具,使用這個工具可以直接對連線在電腦上的手機或模擬器進行除錯操作。它存放在sdk的platform-tools目錄下,如果想要在命令中使用這個工具,就需要把它的路徑配置到環境變數中。由於我使用的是window7,所以右擊我的電腦---》屬性---》高階----》環境變數,然後找到我們當前電腦配置的path路徑,點選編輯,將platform-tools目錄配置進去。
配置好環境變數之後就可以使用adb工具了,開啟命令列介面,輸入adb shell,就會進入到裝置控制檯如下所示:
然後使用cd命令進入到/data/data/com.jack.databasetest/databases/目錄下面,並使用ls命令檢視到該目錄裡的檔案,如下:
這個目錄下面出現了2個數據庫檔案,一個正是我們建立的BookStore.db,而另一個BookStore.db-journal則是為了讓資料庫能夠支援事務而產生的臨時日誌檔案,通常情況下這個檔案的大小都是0位元組。
接下來我們就藉助sqlite命令來開啟資料庫了,只需鍵入sqlite3,後面加上資料庫名即可,如下:
這時已經打開了BookStore.db資料庫,現在就可以對這個資料庫中的表進行管理了,首先來看看目前資料庫中有哪些表,鍵入.table命令,如下所示:
此時資料庫中有2張表,android_metadata是每個資料庫中都會自動生成的,不用管它,而另外一張book表就是我們在MyDatabaseHelper中建立的。這裡我們可以使用.schema命令來檢視它們的建表語句,如下所示:
由上面的操作,我們知道資料庫和表的確已經建立成功了,之後我們鍵入.exit或.quit命令可以退出資料庫的編輯,在鍵入exit命令就可以退出裝置控制檯了。
升級資料庫
MyDatabaseHelper中有一個onUpgrade()方法是用於對資料庫進行升級的,下面我們對資料庫進行升級操作,在資料庫中在新增一張Category表用於記錄書籍的分類。修改 MyDatabaseHelper類中的程式碼,如下所示:
package com.jack.databasetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
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 context;
public MyDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
this.context=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(context, "create succeeded",
Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("drop table if exists book");
db.execSQL("drop table if exists category");
onCreate(db);
}
}
在onUpgrade()方法中執行了2條drop語句,如果發現數據庫中已經存在book表或category表,就將這2張表刪掉,然後在呼叫onCreate()方法重寫建立。接下來修改建立資料庫的版本號,以前的是1,現在我們改成比1大的數,這裡改成2.修改MainActivity中的程式碼,如下:
package com.jack.databasetest;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得資料庫操作類物件
dbHelper=new MyDatabaseHelper(this, "BookStore.db",
null, 2);
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*
* 在onCreate方法中構建了一個MyDatabaseHelper物件,並且通過建構函式的引數
* 將資料庫名指定為BookStore.db,版本號為1,然後在create database按鈕的點選
* 事件裡呼叫了getWritableDatabase()方法,這樣當第一次點選create database按鈕時
* 就會檢測當前程式並沒有BookStore.db這個資料庫,於是會建立該資料庫並呼叫MyDatabaseHelper
* 中的onCreate()方法,這樣Book表也就得到了建立,然後會彈出一個Toast提示建立成功。再次點選
* create database按鈕時,會發現此時已經存在BookStore.db資料庫了,因此不會再建立一次了。
*
* */
dbHelper.getWritableDatabase();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
上面的程式碼,只是把資料的版本改為2了,現在重寫執行程式,並點選按鈕,這時會再次彈出建立成功的提示,在使用adb shell裡面,用.table和.schema檢視如下如下所示,結果:
由此可以看出category表已經建立成功了,同時也說明我們的升級功能,的確起到作用了。
新增資料
資料庫的操作有4種,即CRUD。其中c代表新增create,r代表查詢retrieve,u代表更新update,d代表刪除delete。
呼叫SQLiteOpenHelper的getReadableDatabase()或getWritableDatabase()方法是可以用於建立和升級,不僅如此,這2個方法還都會返回一個SQLiteDatabase物件,藉助這個物件可以對資料庫進行CRUD操作了。
SQLiteDatabase中提供了一個insert()方法,這個方法專門用於新增資料的。它接收3個引數,第一個引數是表名,我們希望向哪張表新增資料,這裡就傳入該表的名字。第二個引數用於在未指定新增資料的情況下給某些可為空的列自動賦值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"
>
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="create database"
/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add data"
/>
</LinearLayout>
修改MainActivity中的程式碼,如下所示:
package com.jack.databasetest;
import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得資料庫操作類物件
dbHelper=new MyDatabaseHelper(this, "BookStore.db",
null, 2);
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*
* 在onCreate方法中構建了一個MyDatabaseHelper物件,並且通過建構函式的引數
* 將資料庫名指定為BookStore.db,版本號為1,然後在create database按鈕的點選
* 事件裡呼叫了getWritableDatabase()方法,這樣當第一次點選create database按鈕時
* 就會檢測當前程式並沒有BookStore.db這個資料庫,於是會建立該資料庫並呼叫MyDatabaseHelper
* 中的onCreate()方法,這樣Book表也就得到了建立,然後會彈出一個Toast提示建立成功。再次點選
* create database按鈕時,會發現此時已經存在BookStore.db資料庫了,因此不會再建立一次了。
*
* */
dbHelper.getWritableDatabase();
}
});
//新增資料
Button addData=(Button) findViewById(R.id.add_data);
addData.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
//開始組裝第一條資料
values.put("name", "the da vinci code");
values.put("author", "jack");
values.put("pages", 500);
values.put("price", 18.98);
db.insert("book", null, values);//插入第一條資料
values.clear();
//開始組裝第二條資料
values.put("name", "the lost symbol");
values.put("author", "jack");
values.put("pages", 600);
values.put("price", 56.87);
db.insert("book", null, values);//插入第二條資料
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
執行程式,點選add data,開啟資料庫,使用select * from book;語句查詢,結果如下:
可以看到剛剛插入的2條資料都已經準確的插入了book表中。
更新資料
SQLiteDatabase中提供了一個非常好用的update()方法用於對資料進行更新,這個方法接收4個引數,第一個引數和insert()方法一樣,也是表名,在這裡指定去更新哪張表裡的資料。第二個引數是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"
>
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="create database"
/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add data"
/>
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="update data"
/>
</LinearLayout>
在修改MainActivity中的程式碼:
package com.jack.databasetest;
import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得資料庫操作類物件
dbHelper=new MyDatabaseHelper(this, "BookStore.db",
null, 2);
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*
* 在onCreate方法中構建了一個MyDatabaseHelper物件,並且通過建構函式的引數
* 將資料庫名指定為BookStore.db,版本號為1,然後在create database按鈕的點選
* 事件裡呼叫了getWritableDatabase()方法,這樣當第一次點選create database按鈕時
* 就會檢測當前程式並沒有BookStore.db這個資料庫,於是會建立該資料庫並呼叫MyDatabaseHelper
* 中的onCreate()方法,這樣Book表也就得到了建立,然後會彈出一個Toast提示建立成功。再次點選
* create database按鈕時,會發現此時已經存在BookStore.db資料庫了,因此不會再建立一次了。
*
* */
dbHelper.getWritableDatabase();
}
});
//新增資料
Button addData=(Button) findViewById(R.id.add_data);
addData.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
//開始組裝第一條資料
values.put("name", "the da vinci code");
values.put("author", "jack");
values.put("pages", 500);
values.put("price", 18.98);
db.insert("book", null, values);//插入第一條資料
values.clear();
//開始組裝第二條資料
values.put("name", "the lost symbol");
values.put("author", "jack");
values.put("pages", 600);
values.put("price", 56.87);
db.insert("book", null, values);//插入第二條資料
}
});
//更新資料
Button updateDate=(Button) findViewById(R.id.update_data);
updateDate.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("price", 99.9);
db.update("book", values, "name=?",
new String[]{"the da vinci code"});
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
執行程式,點選updat data按鈕,在檢視資料庫裡面的book表的內容,如下:
可以看到資料已經改變了,update方法中的第三個引數對應sql語句的where部分,表示更新所有name=?的行,而?是一個佔位符,可以通過第四個引數提供的一個字串資料為第三個引數中的每一個佔位符指定相應的內容。
刪除資料
刪除資料應該是最簡單的,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"
>
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="create database"
/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add data"
/>
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="update data"
/>
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="delete data"
/>
</LinearLayout>
修改MainActivity中的程式碼:
package com.jack.databasetest;
import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得資料庫操作類物件
dbHelper=new MyDatabaseHelper(this, "BookStore.db",
null, 2);
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*
* 在onCreate方法中構建了一個MyDatabaseHelper物件,並且通過建構函式的引數
* 將資料庫名指定為BookStore.db,版本號為1,然後在create database按鈕的點選
* 事件裡呼叫了getWritableDatabase()方法,這樣當第一次點選create database按鈕時
* 就會檢測當前程式並沒有BookStore.db這個資料庫,於是會建立該資料庫並呼叫MyDatabaseHelper
* 中的onCreate()方法,這樣Book表也就得到了建立,然後會彈出一個Toast提示建立成功。再次點選
* create database按鈕時,會發現此時已經存在BookStore.db資料庫了,因此不會再建立一次了。
*
* */
dbHelper.getWritableDatabase();
}
});
//新增資料
Button addData=(Button) findViewById(R.id.add_data);
addData.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
//開始組裝第一條資料
values.put("name", "the da vinci code");
values.put("author", "jack");
values.put("pages", 500);
values.put("price", 18.98);
db.insert("book", null, values);//插入第一條資料
values.clear();
//開始組裝第二條資料
values.put("name", "the lost symbol");
values.put("author", "jack");
values.put("pages", 600);
values.put("price", 56.87);
db.insert("book", null, values);//插入第二條資料
}
});
//更新資料
Button updateDate=(Button) findViewById(R.id.update_data);
updateDate.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("price", 99.9);
db.update("book", values, "name=?",
new String[]{"the da vinci code"});
}
});
//刪除資料
Button deleteButton=(Button) findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
db.delete("book", "pages>?", new String[]{"500"});
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
執行程式,介面如下所示:
刪除資料之前:
點選delete data按鈕之後,再次查詢資料,資料情況如下:
可見,頁數大於500頁的那一條的資料被刪除了
查詢資料:
SQLiteDatabase中提供了一個query()方法用於對資料進行查詢。這個方法的引數非常複雜,最短的一個方法過載也需要傳入7個引數。第一個引數不用說當然是表名了,表示我們希望從哪張表中查詢資料。第二個引數用於指定去查詢哪幾列,如果不指定則預設查詢所有列。第三,第四個引數用於去約束查詢某一行或某幾行的資料,不指定則預設是查詢所有行的資料。第五個引數用於指定需要去group by的列,不指定則表示不對查詢結果進行group by操作。第六個引數用於對group by之後的資料進行進一步的過濾,不指定則表示不進行過濾。第七個引數用於指定查詢結果的排序方式,不指定則表示使用預設的排序方式。
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 | 指定查詢結果的排序方式 |
下面我們進行查詢的操作,首先修改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"
>
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="create database"
/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add data"
/>
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="update data"
/>
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="delete data"
/>
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="query data"
/>
</LinearLayout>
然後修改MainActivity中的程式碼:
package com.jack.databasetest;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得資料庫操作類物件
dbHelper=new MyDatabaseHelper(this, "BookStore.db",
null, 2);
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*
* 在onCreate方法中構建了一個MyDatabaseHelper物件,並且通過建構函式的引數
* 將資料庫名指定為BookStore.db,版本號為1,然後在create database按鈕的點選
* 事件裡呼叫了getWritableDatabase()方法,這樣當第一次點選create database按鈕時
* 就會檢測當前程式並沒有BookStore.db這個資料庫,於是會建立該資料庫並呼叫MyDatabaseHelper
* 中的onCreate()方法,這樣Book表也就得到了建立,然後會彈出一個Toast提示建立成功。再次點選
* create database按鈕時,會發現此時已經存在BookStore.db資料庫了,因此不會再建立一次了。
*
* */
dbHelper.getWritableDatabase();
}
});
//新增資料
Button addData=(Button) findViewById(R.id.add_data);
addData.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
//開始組裝第一條資料
values.put("name", "the da vinci code");
values.put("author", "jack");
values.put("pages", 500);
values.put("price", 18.98);
db.insert("book", null, values);//插入第一條資料
values.clear();
//開始組裝第二條資料
values.put("name", "the lost symbol");
values.put("author", "jack");
values.put("pages", 600);
values.put("price", 56.87);
db.insert("book", null, values);//插入第二條資料
}
});
//更新資料
Button updateDate=(Button) findViewById(R.id.update_data);
updateDate.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("price", 99.9);
db.update("book", values, "name=?",
new String[]{"the da vinci code"});
}
});
//刪除資料
Button deleteButton=(Button) findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
db.delete("book", "pages>?", new String[]{"500"});
}
});
//查詢資料
Button queryButton=(Button) findViewById(R.id.query_data);
queryButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
//查詢book表中的所有資料
Cursor cursor=db.query("book", null, null, null,null, null, null);
/*
* 呼叫moveToFirst()方法將資料的指標移動到第一行的位置,然後進入一個迴圈當中,去遍歷查詢到
* 的每一行資料。在這個迴圈中可以通過Cursor的getColumnIndex()方法取到某一列在表中對應的位置索引,
* 然後將這個索引傳入到相應的取值方法當中,接可以得到從資料庫中查詢到的資料了。接著使用Log的方式將
* 取出來的資料打印出來,藉此來檢查一下讀取工作有沒有成功完成。最後別忘了呼叫close()方法來關閉Cursor。
* */
if(cursor.moveToFirst()){
do{
//遍歷Cursor物件,取出資料並列印
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("MainActivity", "book name is "+name);
Log.d("MainActivity", "book author is "+author);
Log.d("MainActivity", "book pages is "+pages);
Log.d("MainActivity", "book price is "+price);
}while(cursor.moveToNext());
}
cursor.close();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
執行程式,介面如下所示
點選查詢按鈕,打印出如下的內容:
book中的資料已經查詢出來了,這只是簡單的查詢,如果要進行復雜的查詢,當然要用到查詢中的引數了。
使用SQL操作資料庫
雖然android中提供了很多方便的api用於操作資料庫,不過總會有一些人不太習慣,下面我們試試用sql來操作資料庫。
新增資料:
db.execSQL("insert into book(name,author,pages,price) values(?,?,?,?)",
new String[]{"the da vinci code","dan brown","454","17.98"});
db.execSQL("insert into book(name,author,pages,price) values(?,?,?,?)",
new String[]{"the lost symbol","dan brown","600","20.88"});
更新資料:
db.execSQL("update book set price =? where name =?",new String[]{"12.99","the da vinci code"});
刪除資料:
db.execSQL("delete from book where pages>?",new String[]{"500"});
查詢資料的方法如下:
db.rawQuery("select * from book",null);
上面是使用sql語句進行資料的CRUD操作的。
SQLite資料庫的最佳實踐
使用事務:
SQLite資料庫是支援事務的,事務的特性可以保證某一系列的操作要麼全部完成,要麼一個都不會完成。
下面我們試試在android中的事務吧,仍然在DatabaseTest專案的基礎上進行修改。比如book表中的資料都已經很老了,現在準備全部廢棄掉替換成新資料,可以先使用delete方法將book表中的資料刪除,然後在使用insert方法將新的資料新增到列表中。我們要保證的是,刪除舊資料和新增新資料的操作必須一起完成,否則就還要繼續保留原來的舊資料。修改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"
>
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="create database"
/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add data"
/>
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="update data"
/>
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="delete data"
/>
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="query data"
/>
<Button
android:id="@+id/replace_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="replace data"
/>
</LinearLayout>
修改MainActivity中的程式碼:
package com.jack.databasetest;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得資料庫操作類物件
dbHelper=new MyDatabaseHelper(this, "BookStore.db",
null, 2);
Button createDatabase=(Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*
* 在onCreate方法中構建了一個MyDatabaseHelper物件,並且通過建構函式的引數
* 將資料庫名指定為BookStore.db,版本號為1,然後在create database按鈕的點選
* 事件裡呼叫了getWritableDatabase()方法,這樣當第一次點選create database按鈕時
* 就會檢測當前程式並沒有BookStore.db這個資料庫,於是會建立該資料庫並呼叫MyDatabaseHelper
* 中的onCreate()方法,這樣Book表也就得到了建立,然後會彈出一個Toast提示建立成功。再次點選
* create database按鈕時,會發現此時已經存在BookStore.db資料庫了,因此不會再建立一次了。
*
* */
dbHelper.getWritableDatabase();
}
});
//新增資料
Button addData=(Button) findViewById(R.id.add_data);
addData.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
//開始組裝第一條資料
values.put("name", "the da vinci code");
values.put("author", "jack");
values.put("pages", 500);
values.put("price", 18.98);
db.insert("book", null, values);//插入第一條資料
values.clear();
//開始組裝第二條資料
values.put("name", "the lost symbol");
values.put("author", "jack");
values.put("pages", 600);
values.put("price", 56.87);
db.insert("book", null, values);//插入第二條資料
}
});
//更新資料
Button updateDate=(Button) findViewById(R.id.update_data);
updateDate.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("price", 99.9);
db.update("book", values, "name=?",
new String[]{"the da vinci code"});
}
});
//刪除資料
Button deleteButton=(Button) findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
db.delete("book", "pages>?", new String[]{"500"});
}
});
//查詢資料
Button queryButton=(Button) findViewById(R.id.query_data);
queryButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
//查詢book表中的所有資料
Cursor cursor=db.query("book", null, null, null,null, null, null);
/*
* 呼叫moveToFirst()方法將資料的指標移動到第一行的位置,然後進入一個迴圈當中,去遍歷查詢到
* 的每一行資料。在這個迴圈中可以通過Cursor的getColumnIndex()方法取到某一列在表中對應的位置索引,
* 然後將這個索引傳入到相應的取值方法當中,接可以得到從資料庫中查詢到的資料了。接著使用Log的方式將
* 取出來的資料打印出來,藉此來檢查一下讀取工作有沒有成功完成。最後別忘了呼叫close()方法來關閉Cursor。
* */
if(cursor.moveToFirst()){
do{
//遍歷Cursor物件,取出資料並列印
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("MainActivity", "book name is "+name);
Log.d("MainActivity", "book author is "+author);
Log.d("MainActivity", "book pages is "+pages);
Log.d("MainActivity", "book price is "+price);
}while(cursor.moveToNext());
}
cursor.close();
}
});
//android資料庫中的事務處理
Button replaceButton=(Button) findViewById(R.id.replace_data);
replaceButton.setOnClickListener(new OnClickListener(){
/*
* SQLiteDatabase的beginTransaction()方法來開啟一個事務,然後在一個異常捕獲
* 的程式碼中去執行具體的資料庫操作,當所有的操作完成之後,呼叫setTransactionSuccessful(),
* 表示事務已經執行成功了,最後在finally程式碼塊中呼叫endTransaction()來結束事務。
* 注意觀察,我們在剛刪除舊資料的操作完成之後手動丟擲一個NullPointerException,這樣
* 新增新資料的程式碼就得不到執行。不過由於事務的存在,中途出現異常會導致事務的失敗,此時
* 舊資料應該是刪除不掉的。
* */
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
db.beginTransaction();//開啟事務
try{
db.delete("book", null, null);
if(true){
throw new NullPointerException();
}
ContentValues values=new ContentValues();
values.put("name", "jack");
values.put("author", "tom");
values.put("pages", 810);
values.put("price",60.78);
db.insert("book", null, values);
db.setTransactionSuccessful();//事務已經執行成功
}catch(Exception e){
e.printStackTrace();
}finally{
db.endTransaction();//結束事務
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
執行程式並點選replace data按鈕,你會發現book表中存在的還是之前的舊資料。然後將手動丟擲異常的那行程式碼去除掉,在重新執行下程式,此時點選一下replace data按鈕就會將book表中的資料替換成新資料了。
升級資料庫的最佳實戰
在產品上線了的時候,怎麼升級資料庫,並儲存原來的資料呢?下面我們來學習如何升級資料庫。我們知道每一個數據庫都有一個版本號,當指定的資料庫版本號大於當前資料庫版本號的時候,就會進入到onUpgrade()方法中執行更新操作。這裡需要為每一個版本號賦予它各自改變的內容,然後在onUpgrade()方法中對當前資料庫版本號進行判斷,在執行相應的改變就可以了。
下面我們來一個模擬資料庫升級的案例,還是由MyDatabaseHelper類來對資料庫進行管理。第一版的程式程式,只需要建立一張book表,MyDatabaseHelper中的程式碼如下所示:
package com.jack.databasetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
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 context;
public MyDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
this.context=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(CREATE_BOOK);
/*db.execSQL(CREATE_CATEGORY);*/
/*Toast.makeText(context, "create succeeded",
Toast.LENGTH_SHORT).show();*/
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
/*db.execSQL("drop table if exists book");
db.execSQL("drop table if exists category");
onCreate(db);*/
}
}
不過,幾個星期之後又有了新的需求,這次需要向資料庫中在新增一張Category表,於是修改MyDatabaseHelper中的程式碼,如下所示:
package com.jack.databasetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
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 context;
public MyDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
//this.context=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
/*Toast.makeText(context, "create succeeded",
Toast.LENGTH_SHORT).show();*/
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
/*db.execSQL("drop table if exists book");
db.execSQL("drop table if exists category");
onCreate(db);*/
switch(oldVersion){
case 1:
db.execSQL(CREATE_CATEGORY);
default:break;
}
}
}
可以看到在onCreate()方法裡我們新增了一條建表語句,然後又在onUpgrade()方法中添加了一個switch判斷,如果使用者當前資料庫版本號是1,就會建立一張一張category表,這樣當用戶是直接安裝的第二版的程式時,就會將兩張表一起建立。而當用戶是使用第二版的程式覆蓋安裝第一版的程式時,就會進入到資料庫升級的操作中,此時由於book表已經存在了,因此只需要建立一張category表即可。
但是沒過多久,新的需求又來了,這次要給book表和category表之間建立聯絡,需要在book表中新增一個category_id的欄位。再次修改MyDatabaseHelper中的程式碼,如下所示:
package com.jack.databasetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
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, "
+ "category_id integer)";
public static final String CREATE_CATEGORY="create table category("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
//private Context context;
public MyDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
//this.context=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
/*Toast.makeText(context, "create succeeded",
Toast.LENGTH_SHORT).show();*/
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
/*db.execSQL("drop table if exists book");
db.execSQL("drop table if exists category");
onCreate(db);*/
switch(oldVersion){
case 1:
db.execSQL(CREATE_CATEGORY);
case 2:
db.execSQL("alert table book add column category_id integer");
default:break;
}
}
}
可以看到,首先我們在book表的建表語句中添加了一個category_id列,這樣當用戶直接安裝第三版的程式時,這個新增的列就已經自動新增成功了,然而,如果使用者之前已經安裝了某一版本的程式,現在需要覆蓋安裝,就會進入到升級資料庫的操作中。在onUpgrade()方法裡,我們添加了一個新的case,如果當前資料庫版本號是2,就會執行alter命令來為book表新增加一個category_id列。
注意:switch中每個case的最後都是沒有使用break的,這是為了保證在跨資料庫版本升級的時候,每次的資料庫修改都能被全部執行到。比如使用者當前是從第二版程序升級到第三版程式,那麼case 2中的邏輯就會執行。而如果使用者是直接從第一版升級到第三版的程式,那麼case 1和case2中的邏輯都會執行,使用這種方式來維護資料庫的升級不管資料庫怎樣更新,都可以保證資料庫的表結構是最新的,而且表中的資料也完全不會丟失。