編輯:關於Android編程
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/
下面我們通過例子來學習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 } }
修改activity_main.xml中的代碼,如下所示:
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 ExplZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcrLpv7SjrMTju+G3os/WZGF0YWJhc2VzxL/CvM/Cw+az9s/WwcvSu7j2Qm9va1N0b3JlLmRizsS8/qOstavKx2Jvb2ux7crHzt63qM2ouf1GaWxlIEV4cGxvZXK/tLW9tcShozwvcD4KPHA+PGltZyBzcmM9"/uploadfile/Collfiles/20141117/20141117085712132.jpg" alt="\">
由於不能通過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; } }
由此可以看出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中的代碼,如下所示:
修改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; } }
可以看到剛剛插入的2條數據都已經准確的插入了book表中。
更新數據
SQLiteDatabase中提供了一個非常好用的update()方法用於對數據進行更新,這個方法接收4個參數,第一個參數和insert()方法一樣,也是表名,在這裡指定去更新哪張表裡的數據。第二個參數是ContentValues對象,要把更新數據在這裡組裝進去。第三個參數,第四個參數用於約束更新某一行或某幾行中的數據,不指定的話默認就是更新所有行。
修改activity_main.xml中的代碼,如下:
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中的代碼:
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中的代碼,如下所示:
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中的代碼:
修改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; } } }
但是沒過多久,新的需求又來了,這次要給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中的邏輯都會執行,使用這種方式來維護數據庫的升級不管數據庫怎樣更新,都可以保證數據庫的表結構是最新的,而且表中的數據也完全不會丟失。
轉載請注明來自:http://blog.csdn.net/j903829182/article/details/40981205
前段時間群裡兄弟項目中有類似這樣的需求 我看到兄弟受苦受難,於心不忍。又因事不關己,打算高高掛起。正在愛恨糾結之時,日神對我說:沒事多造點輪子,你的人生會有很多
在前面我們在解決線程同步問題的時候使用了synchronized關鍵字,今天我們來看看Java 5.0以後提供的線程鎖Lock.Lock接口的實現類提供了比使用synch
先貼出本文程序運行結果的截圖,上面是播放/停止音頻,可用SeekBar來調進度,下面是播放/停止視頻,也是用SeekBar來調進度: main.xml的源碼:
能夠使頭部懸停的listview在項目中是經常用到的,例如qq的好友列表或者地區選擇。先不多說,看效果圖:(懶得上gif圖了)這裡借鑒了別人的核心代碼,我做了一些分析。主