----------------------------------------SQLite數據庫----------------------------------------------
SQLite是一款輕量級的關系型數據庫,它的運算速度非常快,占用資源很少,通常只需要幾百K的內存就足夠了,因此特別適合在移動設備上使用。
SQLite不僅支持標准的SQL語法,還遵守了數據庫的 ACID 事務,只要你以前使用過其他的關系型數據庫,就可以很快的上手SQLite。
而SQLite又比一般的數據庫要簡單的多,它甚至不用設置用戶名和密碼就可以使用。
Android 正是把這個功能極為強大的數據庫嵌入到了系統當中,使得本地持久化的功能有了一次質的飛躍。
-------------------------------------------創建數據庫-----------------------------------------------
Android 提供了一個SQLiteOpenHelper 幫助類,借助這個類就可以非常簡單的對數據庫進行創建和升級。
SQLiteOpenHelper 是一個抽象類,如果需要使用它的話,就需要創建一個幫助類去繼承它。
SQLiteOpenHelper 中有兩個抽象方法,分別是 onCreate() 和 onUpdate(),需要在幫助類里重寫這兩個方法,然后分別在這兩個方法中去實現創建和升級數據庫的邏輯。
SQLiteOpenHelper 中還有兩個非常重要的實例方法,getReadableDatabase() 和 getWritableDatabase()。這兩種方法都可以創建或打開一個現有的數據庫(如果數據庫已存在則直接打開,否則創建一個新的數據庫),並返回一個可對數據庫進行讀寫操作的對象。不同的是,當數據庫不可寫入的時候(如磁盤空間已滿)getReadableDatabase() 方法返回的對象將以只讀的方式去打開數據庫,而getWritableDatabase() 方法將拋出異常。
SQLiteOpenHelper 的構造方法接收四個參數,第一個參數是 Context,必須要有Context對象才能對數據庫進行操作。第二個參數是數據庫名,創建數據庫時使用的就是這里指定的名稱。第三個參數允許在查詢數據庫的時候返回一個自定義的 Cursor,一般傳入null。第四個參數表示當前數據庫的版本號,可用於對數據庫進行升級操作。
構建出 SQLiteOpenHelper 的實例之后,再調用它的 getReadableDatabase() 或 getWritableDatabase() 方法就能夠創建數據庫了,數據庫文件會存放在 /data/data/<包名>/database/ 目錄下。
新建 MyDatabaseHelper 類繼承自 SQLiteOpenHelper:
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 )"; /** * integer:整形 * real:浮點型 * text:文本類型 * blob:二進制類型 * PRIMARY KEY將id列設置為主鍵 * AutoIncrement關鍵字表示id列是自動增長的 */ private Context myContent; public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); myContent = context; } @Override public void onCreate(SQLiteDatabase db) { //創建數據庫的同時創建Book表 db.execSQL(CREATE_BOOK); //提示數據庫創建成功 Toast.makeText(myContent, "數據庫創建成功", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
在 MainActivity 中進行測試:
public class MainActivity extends AppCompatActivity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //構建一個 MyDatabaseHelper 對象,通過構造函數將數據庫名指定為 BookStore.db dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,1); Button createDatabase = (Button)findViewById(R.id.create_database); createDatabase.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { /** *調用getWritableDatabase() 方法 * 自動檢測當前程序中 BookStore.db 這個數據庫 * 如果不存在則創建該數據庫並調用 onCreate() 方法 * 同時Book表也會被創建 */ dbHelper.getWritableDatabase(); } }); } }
點擊按鈕 BookStore.db 數據庫和 Book 表就已經創建成功了,再次點擊不會再有Toast彈出。
關於如何查看SQLite數據庫,請參考:Android 中 SQLite 數據庫的查看
-------------------------------------------升級數據庫-----------------------------------------------
onUpdate() 方法是用於對數據庫進行升級的,它在整個數據庫的管理工作中擔當着非常重要的作用。
目前 BookStore.db 中已經有一張 Book 表用於存放書的各種詳細數據,接下來再添加一張 Category 表用於記錄書籍的分類。
將建表語句添加到 MyDatabaseHelper 中,代碼如下:
public static final String CREATE_CATEGORY = "CREATE TABLE category (" + "id integer PRIMARY KEY Autoincrement , " + "category_name text , " + "category_code integer )";
並在 onCreate()方法中添加:db.execSQL( CREATE_CATEGORY); 這條語句,運行程序,並不會彈出創建成功的提示。因為此時 BookStore.db 數據庫已經存在了,之后不論怎樣點擊創建按鈕,MyDatabaseHelper 中的 onCreate() 方法都不會再次執行,因此新添加的表也就無法得到創建了。
只需要巧妙的運用 SQLiteOpenHelper 的升級功能就可以很輕松的解決這個問題。
修改 MyDatabaseHelper 中的代碼,如下所示:
@Override public void onCreate(SQLiteDatabase db) { //創建Book表和Category表 db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); //提示數據庫創建成功 Toast.makeText(myContent, "數據庫創建成功", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /** * 如果發現數據庫中已經存在 Book 表或 Category 表 * 就將這兩張表刪除掉,然后調用 onCreate() 方法重新創建 * 如果在創建表時發現表已經存在,就會直接報錯 */ db.execSQL("DROP TABLE IF EXISTS Book"); db.execSQL("DROP TABLE IF EXISTS Category"); onCreate(db); }
接下來的問題就是如何讓 onUpgrade() 方法能夠得到執行了,還記得 SQLiteOpenHelper 的構造方法里接受的第四個參數嗎?它表示當前數據庫的版本號,之前我們傳入的是1,現在只要傳入一個比1大的數,就可以讓 onUpgrade() 方法得到執行了,修改MainActivity 中的代碼:
dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,3);
這里將數據庫版本號指定為3,表示我們對數據庫進行升級了。重新運行程序,並點擊創建數據庫按鈕,這時就會再次彈出創建成功的提示。
升級數據庫的最佳方式:
粗暴的刪除當前所有的表來達到更新的效果,對於用戶來說是非常糟糕的,因為以前程序中存儲的本地數據全部丟失了。其實只需進行一些合理的控制,就可以保證升級數據庫的時候數據並不會丟失了。
每一個數據庫版本都會對應一個版本號,當指定的數據庫版本號大於當前數據庫版本號的時候,就會進入到 onUpgrade()方法中去執行更新操作。這里需要為每一個版本號賦予它各自改變的內容,然后在 onUpgrade()方法中對當前數據庫的版本進行判斷,再執行相應的改變就可以了。
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: db.execSQL(CREATE_CATEGORY); default: } }
注意,switch 中每一個 case 的最后都是沒有使用 break 的,這是為了保證跨版本升級的時候,每一次的數據庫修改都能被全部執行。
使用這種方式來維護數據庫的升級,不管版本怎樣更新,都可以保證數據庫的表結構是最新的,而且表中的數據也完全不會丟失。
--------------------------------------使用SQL操作數據庫-------------------------------------------
現在你已經掌握了創建和升級數據庫的方法,接下來就該學習一下如何對表中的數據進行操作了。
其實我們可以對數據庫進行的操作也就無非四種,添加(Create)、查詢(Rectrieve)、更新(Update),刪除(Delete)。
每一種操作又各自對應了一種SQL命令,添加數據時使用 insert,查詢數據時使用 select,更新數據時使用 update,刪除數據時使用 delete。
而作為一個開發者熟練使用SQL語句是非常有必要的,Android 提供了一系列方法,使得可以直接通過SQL來操作數據庫。
下面簡略演示一下,如何直接使用SQL來完成增刪改查的操作。
添加數據的方法如下:
/** * SQL插入語句: * INSERT INTO Book(name,author,pages,price) VALUES * ("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"});
刪除數據的方法如下:
/** *SQL刪除語句: * DELETE FROM Book WHERE pages > 500; */ db.execSQL("DELETE FROM Book WHERE pages > ?",new String[]{"500"});
更新數據的方法如下:
/** * SQL更新語句: * UPDATE Book SET price = 10.99 WHERE name = "The Da Vinci Code" ; */ db.execSQL("UPDATE Book SET price = ? WHERE name = ?", new String[]{"10.99", "The Da Vinci Code"});
查詢數據的方法如下:
/** * SQL查詢語句: * SELECT * FROM BOOK ; */ db.rawQuery("SELECT * FROM BOOK", null);
注意:除了查詢數據的時候調用的是 SQLiteDatabase 的 rawQuery()方法,其他的操作都是調用的 execSQL()方法。
--------------------------------------------添加數據------------------------------------------------
並非所有開發者都能非常熟練度使用SQL語言,因此 Android 也提供了一系列的輔助性方法,使得在 Android 中即使不去編寫 SQL 語句,也能輕松完成所有的增刪改查操作。
SQLiteDatabase 中提供了一個 insert() 方法,這個方法就是專門用於添加數據的。
insert() 方法接收三個參數,第一個參數是表名,我們希望向哪張表里添加數據,這里就傳入該表的名字。第二個參數用於在未指定添加數據的情況下給某些可為空的列自動賦值NULL,一般用不到這個功能,直接傳入 null 即可。第三個參數是一個 ContentValues 對象,它提供了一系列的 put() 方法重載,用於向 ContentValues 中添加數據,只需要將表中的每個列名及相對應的待添加數據傳入即可。
public void onClick(View v) { //獲取 SQLiteDatabase 對象 SQLiteDatabase db = dbHelper.getWritableDatabase(); //使用ContentValues 對數據進行組裝 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.clear(); //開始組裝第二條數據 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); }
這里只對Book表里其中四列的數據進行了組裝,id並沒有賦值。因為創建表的時候就將 id 列設置為自動增長了,它的值會在入庫的時候自動生成,不需要手動賦值。
--------------------------------------------更新數據------------------------------------------------
SQLiteDatabase 中提供了一個非常好用的 update() 方法用於對數據進行更新,這個方法接收四個參數,第一個參數和 insert()方法一樣,也是表名,在這里指定去更新哪張表里的數據。第二個參數是 ContentValues 對象,把要更新的數據在這里組裝進去。第三、第四個參數用於去約束更新某一行的數據,不指定的話默認就是更新所有行。
public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("price", 10.99); //?是一個占位符,通過字符串數組為每個占位符指定相應的內容 db.update("Book", values, "name = ?", new String[]{"The Da Vinci Code"}); }
--------------------------------------------刪除數據------------------------------------------------
SQLiteDatabase 中提供了一個 delete()方法專門用於刪除數據,這個方法接收三個參數,第一個參數仍然是表名,第二、第三個參數用於約束刪除某一行或某幾行的數據,不指定的話默認就是刪除所有行。
public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("Book", "pages > ?", new String[]{"500"}); }
--------------------------------------------查詢數據------------------------------------------------
SQLiteDatabase 中還提供了一個 query() 方法用於對數據進行查詢。這個方法的參數非常復雜,最短的一個方法重載也需要傳入七個參數。
query()方法參數及對應SQL:
table:指定查詢的表名,對應 from table_name
columns:指定查詢的列名,對應 select column1,column2 ...
selection:指定 where 的約束條件,where column = value
selectionArgs:為 where 中的占位符提供具體的值
groupBy:指定需要分組的列,group by column
having:對分組后的結果進一步約束,having column = value
orderBy:指定查詢結果的排序方式,order by column
雖然 query()方法的參數非常多,但是不必每條查詢語句都指定上所有的參數,多數情況下只需傳入少數幾個參數就可以完成查詢操作了。
調用 query()方法后會返回一個 Cursor 對象,查詢到的所有數據都將從這個對象中取出。
public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); //查詢Book表中的所有數據 Cursor cursor = db.query("Book", null, null, null, null, null, null, null); //遍歷Curosr對象,取出數據並打印 while (cursor.moveToNext()) { 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("woider", "Book Name:" + name + " Author:" + author + " Pages:" + pages + " Price:" + price); } //關閉Cursor cursor.close(); }
執行 query()方法之后會得到一個 Cursor對象,然后通過 Cursor 對象的 moveToNext()方法去遍歷查詢到的每一行數據。在這個循環中可以通過 Cursor 的 getColumnIndex() 方法獲取到某一列在表中對應位置的索引,然后將這個索引傳入到相應的取值方法中,就可以得到從數據庫中讀取到的數據了。最后別忘了調用 close()方法來關閉 Cursor。