1 創建SQLite數據庫
Android系統推薦的創建SQLite數據庫的方法,是創建繼承自SQLiteOpenHelper類的子類,並且重寫其中的兩個抽象方法onCreate()和onUpdate(),然後分別在這兩個方法中去實現創建、升級數據庫的邏輯。
除了上面提到的兩個抽象方法外,SQLiteOpenHelper中還有兩個非常重要的實例方法:getReadableDatabase()和getWriteableDatabase(),SQLiteOpenHelper的子類通過這兩個方法來獲取SQLite數據庫的實例對象,並保證以同步方式訪問。通常情況下getReadableDatabase()和getWriteableDatabase()方法都是創建或打開一個現有的數據庫,並返回一個可對數據庫進行讀寫操作的對象。但在某些情況下,例如磁盤空間已滿,getReadableDatabase()方法返回的對象將以只讀的方式去打開數據庫,而getWriteableDatabase()方法將拋出異常。
SQLiteOpenHelper類常用的方法如下表
public class DBHelper extends SQLiteOpenHelper {
private static String DB_NAME="contacts.db";//數據庫名
private static int DB_VERSIOIN=1;
//創建數據庫
public DBHelper(Context context){
//4個參數:
//1,上下文;2數據庫的名稱;3一般都爲null,用於創建一個Cursor對象;4版本號。
super(context,DB_NAME,null,DB_VERSIOIN);
}
//用於建表,在數據庫第一次創建的時候會調用
//SQLiteDatabase:就是我們要操縱的數據庫對象
@Override
public void onCreate(SQLiteDatabase db) {
String sql= "CREATE TABLE contace (_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(10),phonenumber VARCHAR(12))";
db.execSQL(sql);//exec:execute:執行
}
//當數據庫版本號增加時調用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
只有在真正操作數據庫,如調用getReadableDatabase()或者getWriteableDatabase()方法時,數據庫纔會被創建,創建的數據庫文件位於/data/data/<packagename>/database/目錄下。
多學一招:升級SQLite數據庫。
除了onCreate()方法外,SQLiteOpenHelper中還有一個抽象方法onUpdate(),該方法是用於對數據庫進行升級的,它在整個Android應用數據庫管理工作中起着非常重要的作用。
往往,隨着Android應用程序的版本更新,如果之前的數據庫表結構發生了變化或者添加了新表,這時就需要對數據庫也進行升級。表5-2中已經介紹過,SQLiteOpenHelper類的構造方法接收的其中一個參數是int類型的version,它的含義就是當前數據庫的版本號。下面舉例進行說明。假設在Android應用1.0版本中,使用SQLiteOpenHelper訪問數據庫時,version參數傳入的值爲1,那麼數據庫版本號1就會記錄在數據庫中;當應用程序升級到1.1版本,數據庫往往也需要跟着發生變化,這時1.1版本的程序中就要使用一個大於1的整數作爲參數version的值來創建SQLiteOpenHelper對象,用於訪問新的數據庫,例如可以將version參數設置爲2。當系統在構造SQLiteOpenHelper類的對象時,如果發現傳入的版本號跟之前數據庫記錄的不一致,就會自動調用onUpgrade()方法,使得開發者可以在此方法裏執行數據庫的升級操作。
接下來,通過一個實例演示如何實現SQLite數據庫的升級。首先,新建一個名爲SQLiteUpdate的項目,指定包名爲cn.edu.ayit.sqliteupdate,待項目創建成功後,在包上右擊,並在彈出的快捷菜單中依次選擇New→JavaClass命令
在上述對話框中,在Name文本框中輸入DBHelper,Superclass指定爲android.database.sqlite.SQLiteOpenHelper,然後單擊OK按鈕完成創建,接着修改DBHelper的代碼,具體如下所示:
package com.example.sqliteupdate;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import androidx.annotation.Nullable;
public class DBHelper extends SQLiteOpenHelper {
private static final String TAG="DBHelper";
private static String DB_Name="student.db";//數據庫名
private static int DB_VERSION = 2;
public DBHelper(Context context) {
super(context, DB_Name, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.i(TAG ,"數據庫創建了...");
String sql = "CREATE TABLE student(_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(10),age VARCHAR(4),sex VARCHAR(2))";
db.execSQL(sql);//執行建表的SQL語句
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG ,"數據庫升級了...");
}
}
DBHelper用於建庫、建表和數據庫升級的相關處理,編寫完成後,在MainActivity的onCreate()方法裏添加兩行代碼,具體如下所示。
DBHelper dbHelper = new DBHelper(this);
SQLiteDatabase db = dbHelper.getReadableDatabase();
2數據庫的CRUD
SQLiteDatabase是一個數據庫訪問類,該類封裝了一系列數據庫操作的API,可以對數據進行CRUD操作,即數據庫的增刪改查。
插入數據
向數據庫中插入數據的示例代碼如下所示。
//封裝了一個插入數據的方法
//方法需要傳入ContentValues對象
public long insert(ContentValues values){
//獲取可讀寫的SQliteDatabase對象
SQLiteDatabase db = helper.getWritableDatabase();
//插入一條數據到TABLE_NAME表
long id = db.insert(TABLE_NAME,null,values);
//關閉數據庫
db.close();
return id;
}
通過編寫SQL語句的方式,也可以通過調用execSQL()方法向SQLite數據庫中插入一條新的數據。具體代碼如下:
//需要傳入一條sql 語句
//String sql="INSERT INTO contact ('name','lisi','phonenumber','13912345678') "
public void insert_sql(String sql){
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL(sql);
}
SQLiteDatabase中提供的update()方法,用於對數據進行修改,具體代碼如下:
//修改某個聯繫人的數據
public int update(ContentValues values,String name){
SQLiteDatabase db = helper.getWritableDatabase();
int number = db.update(TABLE_NAME,values,"name=?",new String[]{name});
db.close();
return number;
}
SQL語句更新數據
//String sql="UPDATE contact SET phonenumber='139456789123' where name = 'lisi'"
public void update_sql(String sql){
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL(sql);
}
刪除數據
//刪除某個聯繫人的數據
public int deleteByName(String name){
SQLiteDatabase db = helper.getWritableDatabase();
int number = db.delete(TABLE_NAME,"name= ?",new String[]{name});
db.close();
return number;
}
SQL刪除數據
//String sql="DELETE FROM contact WHERE name='lisi' "
public void deltebyName_sql(String sql){
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL(sql);
}
查詢數據
SQLiteDatabase中的query()方法用於對數據進行查詢。這個方法的參數相對複雜,最短的一個重載方法也需要傳入七個參數
- table:指定查詢的表名,對應SQL語句fromtable_name。
- columns:指定查詢的字段名,對應SQL語句“select column1,column2”。
- selectionArgs:爲where中的佔位符提供具體的值。
- selectionArgs:爲where中的佔位符提供具體的值。
- groupBy:指定需要分組的列,相當於SQL中的group by語句。
- having:相當於SQL中的having子句,對group by後的結果進一步約束。
- orderBy:指定查詢結果的排序方式,對應SQL語句order by column。
//查詢所有記錄,返回Cursor對象。
public Cursor selectAll(){
SQLiteDatabase db =helper.getReadableDatabase();
return db.query(TABLE_NAME,null,null,null,null,null,null);
}
Query()方法的返回值是一個Cursor對象,相當於結果集ResultSet。實際上,Cursor是一個遊標接口,在數據庫操作中作爲返回值。它提供了遍歷查詢結果的方法。
Cursor遊標常用方法
SQLiteDatabase 對象的rawQuery查詢數據方法
//查詢指定聯繫人的記錄,需要傳一個 聯繫人的名字
public Cursor selectAll_sql(String name){
SQLiteDatabase db =helper.getReadableDatabase();
String sql = "Select * FROM contact WHERE name=?";
return db.rawQuery(sql,new String[]{name});
}
execSQL()方法通過執行一條SQL語句來完成增刪改的操作,但這個方法沒有返回值。而insert()、update()和delete()方法都有返回結果,分別表示新插入的記錄對應的行號以及更新和刪除操作影響的記錄條數。
3SQLite事務操作
數據庫事務(Transaction)是併發控制的基本單位。所謂的事務,指的是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位。例如,銀行轉賬操作,從一個賬號扣款並使另一個賬號增款,只有這兩部分都完成才認爲轉賬成功,如果其中一個操作出現異常執行失敗,則會導致兩個賬戶的金額不同步。因此,必須做到這兩個操作要麼都執行,要麼都不執行。所以,應該把它們看成一個事務。事務是數據庫維護數據一致性的單位,在每個事務結束時,都能保持數據一致性。
SQLite中當然引入了事務,接下來通過下面的示例代碼來模擬銀行的轉賬業務,演示如何執行一個事務操作。
上述代碼中,首先得到一個可寫的SQLiteDatabase對象,然後使用SQLiteDatabase的beginTransaction()方法去開啓一個事務,程序執行到endTransaction()方法時會檢查事務的標誌是否爲成功,如果程序執行到endTransaction()之前調用了setTransactionSuccessful()方法設置事務的標誌爲成功,則所有從beginTransaction()方法開始的操作都會被提交,如果沒有調用setTransactionSuccessful()方法則回滾事務。最後需要關閉事務,如果不關閉事務,則事務只有等到超時纔會自動結束,會降低數據庫併發執行的效率。
4我的通訊簿實例
(1)新建一個名爲MyContactBook的項目,然後在項目中添加一個DBHelper類用於創建數據庫和建立數據庫表,具體代碼如下所示。
package com.example.mycontackbook;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper {
private static String DB_NAME="contacts.db";//數據庫名
private static int DB_VERSIOIN=1;
//創建數據庫
public DBHelper(Context context){
//4個參數:
//1,上下文;2數據庫的名稱;3一般都爲null,用於創建一個Cursor對象;4版本號。
super(context,DB_NAME,null,DB_VERSIOIN);
}
//用於建表,在數據庫第一次創建的時候會調用
//SQLiteDatabase:就是我們要操縱的數據庫對象
@Override
public void onCreate(SQLiteDatabase db) {
String sql= "CREATE TABLE contace (_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(10),phonenumber VARCHAR(12))";
db.execSQL(sql);//exec:execute:執行
}
//當數據庫版本號增加時調用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
(2)在項目指定的包cn.edu.ayit.mycontactbook下新建一個dao(DataAccessObject,即數據訪問對象)包,並在dao包下創建一個ContactDao類,然後把對contact表進行CRUD的操作封裝在這個類中。具體代碼如下所示。
package com.example.mycontackbook.dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.example.mycontackbook.DBHelper;
public class ContactDao {
private Context mContext;
private DBHelper helper;
private static String TABLE_NAME="contact";
//生成DBHelper類
public ContactDao(Context context){
this.mContext =context;
helper =new DBHelper(mContext);
}
//封裝了一個插入數據的方法
//方法需要傳入ContentValues對象
public long insert(ContentValues values){
//獲取可讀寫的SQliteDatabase對象
SQLiteDatabase db = helper.getWritableDatabase();
//插入一條數據到TABLE_NAME表
long id = db.insert(TABLE_NAME,null,values);
//關閉數據庫
db.close();
return id;
}
//需要傳入一條sql 語句
//String sql="INSERT INTO contact ('name','lisi','phonenumber','13912345678') "
public void insert_sql(String sql){
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL(sql);
}
//刪除全部數據
public int deleteAll(){
SQLiteDatabase db = helper.getWritableDatabase();
int number = db.delete(TABLE_NAME,null,null);
db.close();
return number;
}
//刪除某個聯繫人的數據
public int deleteByName(String name){
SQLiteDatabase db = helper.getWritableDatabase();
int number = db.delete(TABLE_NAME,"name= ?",new String[]{name});
db.close();
return number;
}
//String sql="DELETE FROM contact WHERE name='lisi' "
public void deltebyName_sql(String sql){
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL(sql);
}
//修改某個聯繫人的數據
public int update(ContentValues values,String name){
SQLiteDatabase db = helper.getWritableDatabase();
int number = db.update(TABLE_NAME,values,"name=?",new String[]{name});
db.close();
return number;
}
//String sql="UPDATE contact SET phonenumber='139456789123' where name = 'lisi'"
public void update_sql(String sql){
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL(sql);
}
//查詢所有記錄,返回Cursor對象。
public Cursor selectAll(){
SQLiteDatabase db =helper.getReadableDatabase();
return db.query(TABLE_NAME,null,null,null,null,null,null);
}
//查詢指定聯繫人的記錄,需要傳一個 聯繫人的名字
public Cursor selectAll_sql(String name){
SQLiteDatabase db =helper.getReadableDatabase();
String sql = "Select * FROM contact WHERE name=?";
return db.rawQuery(sql,new String[]{name});
}
}
(3)修改MainActivity的佈局文件,具體代碼如下所示。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="我的通訊薄"
android:textColor="#000000"
android:textSize="26sp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓 名:"
android:textColor="#000000"
android:textSize="20sp"/>
<EditText
android:id="@+id/et_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="聯繫人姓名"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="聯繫方式:"
android:textColor="#000000"
android:textSize="20sp"/>
<EditText
android:id="@+id/et_phone_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="聯繫方式"
android:inputType="number"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_insert"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="添加"/>
<Button
android:id="@+id/btn_delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="刪除"/>
<Button
android:id="@+id/btn_update"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="修改"/>
<Button
android:id="@+id/btn_query"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="查詢"/>
</LinearLayout>
<TextView
android:id="@+id/tv_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"/>
</LinearLayout>
主界面預覽效果
(4)佈局文件編寫完成後,需要在MainActivity中實現處理邏輯,具體代碼如下所示。
package com.example.mycontactbook;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.example.mycontactbook.dao.ContactDao;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText etName;
private EditText etPhoneNumber;
private Button btnInsert;
private Button btnDelete;
private Button btnUpdate;
private Button btnQuery;
private TextView tvList;
//Data Access Object:對contact表進行CRUD操作的類
private ContactDao dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
initView();
dao = new ContactDao(this);
}
private void initView(){
etName= (EditText) findViewById(R.id.et_name);
etPhoneNumber=(EditText)findViewById(R.id.et_phone_number);
btnInsert=(Button) findViewById(R.id.btn_insert);
btnDelete=(Button) findViewById(R.id.btn_delete);
btnUpdate=(Button) findViewById(R.id.btn_update);
btnQuery=(Button) findViewById(R.id.btn_query);
tvList=(TextView)findViewById(R.id.tv_list);
//爲按鈕註冊監聽
btnInsert.setOnClickListener(this);
btnDelete.setOnClickListener(this);
btnUpdate.setOnClickListener(this);
btnQuery.setOnClickListener(this);
}
@Override
public void onClick(View v) {
String name=null;
String phoneNum=null;
switch(v.getId()){
case R.id.btn_insert://添加數據
name=etName.getText().toString().trim();
phoneNum=etPhoneNumber.getText().toString().trim();
if(!TextUtils.isEmpty(name) && !TextUtils.isEmpty(phoneNum)){
ContentValues values = new ContentValues();
values.put("name",name);
values.put("phonenumber",phoneNum);
if(dao.insert(values) >0){
Toast.makeText(this,"聯繫方式添加成功",Toast.LENGTH_SHORT).show();
etName.setText("");
etPhoneNumber.setText("");
}
}
break;
case R.id.btn_delete://刪除數據
name=etName.getText().toString().trim();
//如果etName不爲空則刪除名字叫這個的記錄
if(!TextUtils.isEmpty(name)){
if(dao.deleteByName(name) > 0){
Toast.makeText(this,"刪除聯繫人成功",Toast.LENGTH_SHORT).show();
}
}else{
if(dao.deleteAll() >0){
Toast.makeText(this,"成功刪除所有聯繫人",Toast.LENGTH_SHORT).show();
}
}
break;
case R.id.btn_update://更新數據
name=etName.getText().toString().trim();
phoneNum=etPhoneNumber.getText().toString().trim();
if(!TextUtils.isEmpty(name) && !TextUtils.isEmpty(phoneNum)){
ContentValues values = new ContentValues();
values.put("phonenumber",phoneNum);
if(dao.update(values,name)>0){
Toast.makeText(this,"修改聯繫人方式成功",Toast.LENGTH_SHORT).show();
etName.setText("");
etPhoneNumber.setText("");
}
}
break;
case R.id.btn_query://查詢數據
Cursor cursor = dao.selectAll();
if(cursor.getCount() == 0){
tvList.setText("");
}else {
cursor.moveToFirst();
tvList.setText(cursor.getString(1) + ":" + cursor.getString(2));
}
while (cursor.moveToNext()){
tvList.append("\n" + cursor.getString(1) + ":" + cursor.getString(2) );
}
cursor.close();
break;
}
}
}
上述代碼用於實現聯繫人信息的添加、刪除、修改和查詢。當單擊“添加”按鈕時,會將輸入的聯繫人姓名和聯繫方式存入數據庫中。單擊“刪除”按鈕時,將按兩種情況分別進行處理:一種是輸入了聯繫人姓名,則只會刪除指定聯繫人的信息;另一種是聯繫人姓名爲空,則會刪除數據庫中所有聯繫人的數據。單擊“修改”按鈕,會根據輸入的聯繫人姓名修改對應的聯繫方式。單擊“查詢”按鈕之後將會把所有聯繫人的具體信息展示在界面中的TextView控件裏。