Android--數據持久化之SQLite


前言

  對於一個應用程序而言,數據持久化是必不可少的,Android程序也不例外。這篇博客將介紹Android中關於SQLite的使用,SQLite是一種嵌入式的數據庫引擎,專門適用於資源有限的設備上進行適量的數據存儲,而Android就全面支持標准的SQLite數據庫。在本片博客中,將說明SQLite數據庫的創建以及維護,還有使用SQLite執行CRUD的兩種方式,以及SQLite中事務的使用,最后都會使用示例講解博客中所提到的概念性的內容。

SQLite

  Android對SQLite數據庫,提供了完全的支持,而所有創建的SQLite數據庫,僅限於當前應用訪問,如果其他應用需要訪問,則必須提供的Content Provider的支持,並且SQLite數據庫會隨着Android應用的卸載而被刪除。SQLite是一個嵌入式的數據庫引擎,最后是以文件的形式保存數據的。從本質上來看,SQLite的操作方式只是一種更為便捷的文件操作,當應用程序創建或打開一個SQLite數據庫時,其實只是打開一個文件准備讀寫。因為SQLite僅適用於資源有限的小型設備,所以本身就不應該把大量數據存儲在設備的SQLite數據庫里,SQLite只適合存儲一些小型的數據。

   為了使SQLite和其他數據庫間的兼容性最大化,SQLite支持對列上類型進行“類型近似”,列的類型近似指的是存儲在列上的數據進行推薦類型存儲。所以雖然SQLite內部只支持NULL、INTEGER、REAL(浮點書)、TEXT(文本)和BLOB(大二進制對象)這五種數據類型,但實際上SQLite完全可以接受varchar(n)、char(n)、decimal(p,s)、date等類型數據,只不過SQLite會在運算或保存時將它們轉換為上面五種數據類型中相應的類型。大多數數據庫的引擎都是使用靜態的、強類型的數據類型,數據的類型是由它的容器決定的,這個容器是指被存放的特定列。而SQLite使用的是動態類型,在SQLite中,值的數據類型跟值本身相關,而不是與它的容器相關,所以SQLite允許把各種類型的數據保存到任何類型字段中,開發者可以不用關心聲明該字段說使用的數據類型。但是有一種情況例外,定義為INTEGER PRIMARY KEY的字段只能存儲64位整數,當向這種字段保存除整數意外的其他類型的數據時,SQLite會產生錯誤。

SQLite數據庫創建與維護

  從官方文檔上了解到,在Android項目中,創建SQLite數據庫推薦繼承SQLiteOpenHelper類,然后重寫其中的onCreate()方法,在onCreate()方法中,對執行數據庫創建的SQL語句。而SQLiteOpenHelper不僅僅用於SQLite數據的創建,還可以對其進行維護,以及獲得SQLiteDatabase這個數據庫操作對象。

  SQLiteOpenHelper提供了兩個構造器,用於傳遞當前上下文對象以及SQLite數據庫版本信息,在SQLiteOpenHelper的繼承類的構造函數中,會調用它,構造器的簽名如下:

  • SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version).
  • SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactroy factory,int version,DatabaseErrorHandler errorHandler).

  上面的構造函數中,都是用於創建一個SQLite數據庫,context為一個當前應用的上下文對象;name是數據庫名稱;factory是一個允許子類在查詢時使用的游標,一般不用傳Null;version是數據庫版本號;errorHandler是一個接口,傳遞當數據庫錯誤的時候,執行的補救方法。

  在SQLiteOpenHelper中,可以進行SQLite數據庫的創建、維護、日志以及獲取可讀寫的數據庫對象,通過下面幾個常用方法得到支持:

  • String getDatabaseName():獲取數據庫名。
  • SQLiteDatabase getReadableDatabase():創建或者打開一個可讀的數據庫對象。
  • SQLiteDatabase getWritableDatabase():創建或者打開一個可讀/寫的數據庫對象。
  • abstract void onCreate(SQLiteDatabase db):當第一次調用SQLiteOpenHelper的時候執行,之后再次調用將不再執行,一般用於完成數據庫初始化的工作。
  • void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion):當數據庫版本號發生向上更新時,被執行。
  • void onDowngrade(SQLiteDatabase db,int oldVersion,int newVersion):當數據庫版本號發生向下更新時,被執行。

  下面提供一個簡單的SQLiteOpenHelper的繼承類代碼,用於創建數據庫以及表結構:

 1 package com.example.sqlitedbdemo.db;
 2 
 3 import android.content.Context;
 4 import android.database.sqlite.SQLiteDatabase;
 5 import android.database.sqlite.SQLiteOpenHelper;
 6 
 7 public class DbOpenHelper extends SQLiteOpenHelper {
 8     private static String name = "mydb.db";
 9     private static int version = 1;
10 
11     public DbOpenHelper(Context context) {
12         super(context, name, null, version);
13     }
14 
15     @Override
16     public void onCreate(SQLiteDatabase db) {
17         // 只能支持基本數據類型
18         String sql = "create table person(id integer primary key autoincrement,name varchar(64),address varchar(64))";
19         db.execSQL(sql);
20     }
21     @Override
22     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
23         // TODO Auto-generated method stub
24         String sql="alter table person add sex varchar(8)";
25         db.execSQL(sql);
26     }
27 }

   Tips:當創建好SQLite數據庫的之后,可以在/data/data/<package name>/databases目錄下找到SQLite數據庫文件。

執行CRUD操作

  當使用SQLiteOpenHelper的getReadableDatabase()或者getWritableDatabase()方法獲取到SQLiteDatabase對象,就可以對這個數據庫進行操作了。

  對於熟悉SQL語句的開發者而言,其實只需要使用兩個方法,即可執行所有CRUD操作,以下方法提供多個重載方法:

  • void execSQL():通過SQL語句執行一條非查詢語句。
  • Cursor rawQuery():通過SQL語句執行一條查詢語句。

  下面以一個示例講解一下單純使用SQL語句實現CRUD操作:

  接口代碼:

 1 package com.examle.sqlitedbdemo.service;
 2 
 3 import java.util.List;
 4 import java.util.Map;
 5 
 6 public interface PersonService {
 7 
 8     public boolean addPerson(Object[] params);
 9     public boolean deletePerson(Object[] params);
10     public boolean updatePerson(Object[] params);
11     public Map<String, String> viewPerson(String[] selectionArgs);
12     public List<Map<String, String>> listPersonMaps(String[] selectionArgs);
13 }

  接口的實現代碼:

  1 package com.examle.sqlitedbdemo.dao;
  2 
  3 import java.util.ArrayList;
  4 import java.util.HashMap;
  5 import java.util.List;
  6 import java.util.Map;
  7 
  8 import android.content.Context;
  9 import android.database.Cursor;
 10 import android.database.sqlite.SQLiteDatabase;
 11 
 12 import com.examle.sqlitedbdemo.service.PersonService;
 13 import com.example.sqlitedbdemo.db.DbOpenHelper;
 14 
 15 public class PersonDao implements PersonService {
 16     private DbOpenHelper helper = null;
 17 
 18     public PersonDao(Context context) {
 19         helper = new DbOpenHelper(context);
 20     }
 21 
 22     @Override
 23     public boolean addPerson(Object[] params) {
 24         boolean flag = false;
 25         SQLiteDatabase database = null;
 26         try {
 27             // insert一條數據
 28             String sql = "insert into person(name,address,sex) values(?,?,?)";
 29             database = helper.getWritableDatabase();
 30             // 執行SQL
 31             database.execSQL(sql, params);
 32             flag = true;
 33         } catch (Exception e) {
 34             e.printStackTrace();
 35         } finally {
 36             if (database != null) {
 37                 // finally中關閉數據庫
 38                 database.close();
 39             }
 40         }
 41         return flag;
 42     }
 43 
 44     @Override
 45     public boolean deletePerson(Object[] params) {
 46         boolean flag = false;
 47         SQLiteDatabase database = null;
 48         try {
 49             // 刪除一條數據
 50             String sql = "delete from person where id=?";
 51             database = helper.getWritableDatabase();
 52             database.execSQL(sql, params);
 53             flag = true;
 54         } catch (Exception e) {
 55             e.printStackTrace();
 56         } finally {
 57             if (database != null) {
 58                 database.close();
 59             }
 60         }
 61         return flag;
 62     }
 63 
 64     @Override
 65     public boolean updatePerson(Object[] params) {
 66         boolean flag = false;
 67         SQLiteDatabase database = null;
 68         try {
 69             // 更新一條數據
 70             String sql = "update person set name=?,address=?,sex=? where id=?";
 71             database = helper.getWritableDatabase();
 72             // 執行SQL
 73             database.execSQL(sql, params);
 74             flag = true;
 75         } catch (Exception e) {
 76             e.printStackTrace();
 77         } finally {
 78             if (database != null) {
 79                 database.close();
 80             }
 81         }
 82         return flag;
 83     }
 84 
 85     @Override
 86     public Map<String, String> viewPerson(String[] selectionArgs) {
 87         Map<String, String> map = new HashMap<String, String>();
 88         SQLiteDatabase database = null;
 89         try {
 90             // 查詢單條記錄
 91             String sql = "select * from person where id=?";
 92             // 以只讀的形式打開數據庫
 93             database = helper.getReadableDatabase();
 94             // 執行SQL語句,返回一個游標
 95             Cursor cursor = database.rawQuery(sql, selectionArgs);
 96 
 97             int colums = cursor.getColumnCount();
 98             while (cursor.moveToNext()) {
 99                 for (int i = 0; i < colums; i++) {
100                     String cols_name = cursor.getColumnName(i);
101                     String cols_value = cursor.getString(cursor
102                             .getColumnIndex(cols_name));
103                     if (cols_value == null) {
104                         cols_value = "";
105                     }
106                     map.put(cols_name, cols_value);
107                 }
108             }
109         } catch (Exception e) {
110             e.printStackTrace();
111         } finally {
112             if (database != null) {
113                 database.close();
114             }
115         }
116         return map;
117     }
118 
119     @Override
120     public List<Map<String, String>> listPersonMaps(String[] selectionArgs) {
121         List<Map<String, String>> list = new ArrayList<Map<String, String>>();
122         String sql = "select * from person";
123         SQLiteDatabase database = null;
124         try {
125             database = helper.getReadableDatabase();
126             Cursor cursor = database.rawQuery(sql, selectionArgs);
127             int colums = cursor.getColumnCount();
128             while (cursor.moveToNext()) {
129                 Map<String, String> map = new HashMap<String, String>();
130                 for (int i = 0; i < colums; i++) {
131                     String cols_name = cursor.getColumnName(i);
132                     String cols_value = cursor.getString(cursor
133                             .getColumnIndex(cols_name));
134                     if (cols_value == null) {
135                         cols_value = "";
136                     }
137                     map.put(cols_name, cols_value);
138                 }
139                 list.add(map);
140             }
141         } catch (Exception e) {
142             e.printStackTrace();
143         } finally {
144             if (database != null) {
145                 database.close();
146             }
147         }
148         return list;
149     }
150 }

   再寫一個測試類測試這個數據操作類是否有效,Android下JUnit的配置參見另外一篇博客:Android--JUnit單元測試

 1 package com.example.sqlitedbdemo.db;
 2 
 3 import java.util.List;
 4 import java.util.Map;
 5 
 6 import com.examle.sqlitedbdemo.dao.PersonDao;
 7 import com.examle.sqlitedbdemo.service.PersonService;
 8 
 9 import android.test.AndroidTestCase;
10 import android.util.Log;
11 
12 public class TestDb extends AndroidTestCase {
13     private final String TAG = "main";
14 
15     public TestDb() {
16         // TODO Auto-generated constructor stub
17     }
18 
19     public void createDB() {
20         DbOpenHelper helper = new DbOpenHelper(getContext());
21         helper.getWritableDatabase();
22     }
23 
24     public void insertDb() {
25         PersonService service = new PersonDao(getContext());
26         Object[] params1 = { "張龍", "beijing", "male" };
27         boolean flag = service.addPerson(params1);
28         Object[] params2 = { "趙虎", "shanghai", "male" };
29         flag = flag&&service.addPerson(params2);
30         Object[] params3 = { "王朝", "HK", "male" };
31         flag = flag&&service.addPerson(params3);
32         Object[] params4 = { "馬漢", "beijing", "female" };
33         flag = flag&&service.addPerson(params4);
34         Log.i(TAG, "-----插入數據----->>" + flag);
35     }
36 
37     public void deleteDb() {
38         PersonService service = new PersonDao(getContext());
39         Object[] params = { 1 };
40         boolean flag = service.deletePerson(params);
41         Log.i(TAG, "-----刪除數據----->>" + flag);
42     }
43 
44     public void updateDb() {
45         PersonService service=new PersonDao(getContext());
46         Object[] params = { "張三", "上海", "男","2" };
47         boolean flag=service.updatePerson(params);
48         Log.i(TAG, "---------->>" + flag);
49     }
50     
51     public void getDb(){
52         PersonService service=new PersonDao(getContext());
53         Map<String, String> map = service.viewPerson(new String[]{"2"});
54         Log.i(TAG, "---------->>" + map.toString());
55     }
56     
57     public void listDb() {
58         PersonService service = new PersonDao(getContext());
59         List<Map<String, String>> list = service.listPersonMaps(null);
60         Log.i(TAG, "---------->>" + list.toString());
61     }
62 }

  insertDB()后,如果是在模擬器上調試,可以使用FIle Explorer工具導出mydb.db文件,使用SQLite Expert Professional(這是一個SQLite的管理軟件,博客最后提供下載地址),打開數據庫:

  執行deleteDb()刪除第一條數據:

  執行updateDb()更新第二條數據:

  執行getDb(),查詢第二條數據,執行listDb(),查詢全部數據,查看日志輸出:

  

  而如果是從事Android開發,還有必要了解另外一種操作SQLite的方式,使用SQLiteDatabase所提供的方法實現CRUD操作。主要有以下幾個方法:

  • long insert(String table ,String nullColumnHack,ContentValues values):插入一條數據。
  • int delete(String table ,String whereCaluse,String[] whereArgs):根據條件,刪除數據。
  • int updata(String table,ContentValues values,String whereCaluse,String[] whereArgs):根據條件,更新數據
  • Cursor query(...):根據條件,查詢數據。提供多種重載方法,主要查詢不同的條件。

  下面以一個示例程序講解一下使用SQLiteDatabase所提供的方法實現CRUD操作:

  接口代碼:

 1 package com.examle.sqlitedbdemo.service;
 2 
 3 import java.util.List;
 4 import java.util.Map;
 5 
 6 import android.content.ContentValues;
 7 
 8 public interface PersonService2 {
 9 
10     public boolean addPerson(ContentValues values);
11 
12     public boolean deletePerson(String whereClause, String[] whereArgs);
13 
14     public boolean updatePerson(ContentValues values, String whereClause,
15             String[] whereArgs);
16 
17     public Map<String, String> viewPerson(String selection,
18             String[] selectionArgs);
19 
20     public List<Map<String, String>> listPersonMaps(String selection,
21             String[] selectionArgs);
22 }

  實現代碼:

  1 package com.examle.sqlitedbdemo.dao;
  2 
  3 import java.util.ArrayList;
  4 import java.util.HashMap;
  5 import java.util.List;
  6 import java.util.Map;
  7 
  8 import android.content.ContentValues;
  9 import android.content.Context;
 10 import android.database.Cursor;
 11 import android.database.sqlite.SQLiteDatabase;
 12 
 13 import com.examle.sqlitedbdemo.service.PersonService2;
 14 import com.example.sqlitedbdemo.db.DbOpenHelper;
 15 
 16 public class PersonDao2 implements PersonService2 {
 17     private DbOpenHelper helper = null;
 18 
 19     public PersonDao2(Context context) {
 20         helper = new DbOpenHelper(context);
 21     }
 22 
 23     @Override
 24     public boolean addPerson(ContentValues values) {
 25         boolean flag = false;
 26         SQLiteDatabase database = null;
 27         long id = -1;
 28         try {
 29             database = helper.getWritableDatabase();
 30             // 執行insert,返回當前行ID
 31             id = database.insert("person", null, values);
 32             flag = (id != -1 ? true : false);
 33         } catch (Exception e) {
 34             e.printStackTrace();
 35         } finally {
 36             if (database != null) {
 37                 database.close();
 38             }
 39         }
 40         return flag;
 41     }
 42 
 43     @Override
 44     public boolean deletePerson(String whereClause, String[] whereArgs) {
 45         boolean flag = false;
 46         SQLiteDatabase database = null;
 47         int count = 0;
 48         try {
 49             database = helper.getWritableDatabase();
 50             // 執行刪除操作,返回影響行數
 51             count = database.delete("person", whereClause, whereArgs);
 52             flag = (count > 0 ? true : false);
 53         } catch (Exception e) {
 54             e.printStackTrace();
 55         } finally {
 56             if (database != null) {
 57                 database.close();
 58             }
 59         }
 60         return flag;
 61     }
 62 
 63     @Override
 64     public boolean updatePerson(ContentValues values, String whereClause,
 65             String[] whereArgs) {
 66         boolean flag = false;
 67         SQLiteDatabase database = null;
 68         int count = 0;
 69         try {
 70             database = helper.getWritableDatabase();
 71             // 執行更新操作,返回影響行數
 72             count = database.update("person", values, whereClause, whereArgs);
 73             flag = (count > 0 ? true : false);
 74         } catch (Exception e) {
 75             e.printStackTrace();
 76         } finally {
 77             if (database != null) {
 78                 database.close();
 79             }
 80         }
 81         return flag;
 82     }
 83 
 84     @Override
 85     public Map<String, String> viewPerson(String selection,
 86             String[] selectionArgs) {
 87         SQLiteDatabase database = null;
 88         Cursor cursor = null;
 89         Map<String, String> map = new HashMap<String, String>();
 90         try {
 91             database = helper.getReadableDatabase();
 92             // 設置查詢條件
 93             cursor = database.query(true, "person", null, selection,
 94                     selectionArgs, null, null, null, null);
 95             int cols_len = cursor.getColumnCount();
 96             while (cursor.moveToNext()) {
 97                 for (int i = 0; i < cols_len; i++) {
 98                     String cols_key = cursor.getColumnName(i);
 99                     String cols_value = cursor.getString(cursor
100                             .getColumnIndex(cols_key));
101                     if (cols_value == null) {
102                         cols_value = "";
103                     }
104                     map.put(cols_key, cols_value);
105                 }
106             }
107         } catch (Exception e) {
108             e.printStackTrace();
109         }
110         return map;
111     }
112 
113     @Override
114     public List<Map<String, String>> listPersonMaps(String selection,
115             String[] selectionArgs) {
116         List<Map<String, String>> list = new ArrayList<Map<String, String>>();
117         SQLiteDatabase database = null;
118         Cursor cursor = null;
119         try {
120             database = helper.getReadableDatabase();
121             cursor = database.query(false, "person", null, selection,
122                     selectionArgs, null, null, null, null);
123             int cols_len = cursor.getColumnCount();
124             while (cursor.moveToNext()) {
125                 Map<String, String> map = new HashMap<String, String>();
126                 for (int i = 0; i < cols_len; i++) {
127                     String cols_key = cursor.getColumnName(i);
128                     String cols_value = cursor.getString(cursor
129                             .getColumnIndex(cols_key));
130                     if (cols_value == null) {
131                         cols_value = "";
132                     }
133                     map.put(cols_key, cols_value);
134                 }
135                 list.add(map);
136             }
137         } catch (Exception e) {
138             e.printStackTrace();
139         }
140         return list;
141     }
142 
143 }

  最后和上面一下,創建一個測試類來測試這個數據庫操作:

 1 package com.example.sqlitedbdemo.db;
 2 
 3 import java.util.List;
 4 import java.util.Map;
 5 
 6 import com.examle.sqlitedbdemo.dao.PersonDao2;
 7 import com.examle.sqlitedbdemo.service.PersonService2;
 8 
 9 import android.content.ContentValues;
10 import android.test.AndroidTestCase;
11 import android.util.Log;
12 
13 public class TestDb2 extends AndroidTestCase {
14     private final String TAG = "main";
15 
16     public TestDb2() {
17         // TODO Auto-generated constructor stub
18     }
19 
20     public void addPerson() {
21         PersonService2 service2 = new PersonDao2(getContext());
22         ContentValues values1 = new ContentValues();
23         values1.put("name", "張龍");
24         values1.put("address", "beijing");
25         values1.put("sex", "male");
26         boolean flag = service2.addPerson(values1);
27         ContentValues values2 = new ContentValues();
28         values2.put("name", "趙虎");
29         values2.put("address", "shanghai");
30         values2.put("sex", "male");
31         flag = flag&&service2.addPerson(values2);
32         ContentValues values3 = new ContentValues();
33         values3.put("name", "王朝");
34         values3.put("address", "HK");
35         values3.put("sex", "male");
36         flag = flag&&service2.addPerson(values3);
37         ContentValues values4 = new ContentValues();
38         values4.put("name", "王朝");
39         values4.put("address", "HK");
40         values4.put("sex", "male");
41         flag = flag&&service2.addPerson(values4);
42         Log.i(TAG, "----------->>" + flag);
43     }
44     
45     public void deletePerson() {
46         PersonService2 service2 = new PersonDao2(getContext());
47         boolean flag = service2.deletePerson(" id =?", new String[]{"1"});
48         Log.i(TAG, "----------->>" + flag);
49     }
50     
51     public void updatePerson(){
52         PersonService2 service2 = new PersonDao2(getContext());
53         ContentValues values = new ContentValues();
54         values.put("name", "張三"); 
55         values.put("address", "上海");
56         values.put("sex", "男");
57         boolean flag=service2.updatePerson(values, " id=? ", new String[]{"2"});
58         Log.i(TAG, "----------->>" + flag);
59     }
60     
61     public void viewPerson(){
62         PersonService2 service2 = new PersonDao2(getContext());
63         Map<String, String> map=service2.viewPerson(" id=? ", new String[]{"2"});
64         Log.i(TAG, "----------->>" + map.toString());
65     }
66     public void listPerson(){
67         PersonService2 service2 = new PersonDao2(getContext());
68         List<Map<String, String>> list=service2.listPersonMaps(null,null);
69         Log.i(TAG, "----------->>" + list.toString());
70     }
71 }

  實現的功能和上面一樣,這里就不展示效果圖了,但是因為是上面兩種操作數據庫的方式是在一個應用中完成的,並且數據一樣,執行第二個測試類的時候,需要把之前創建的數據庫刪除,詳情參見源碼。 

 SQLite事務

  SQLite的事務通過SQLiteDatabase中包含的兩個方法對其進行控制:beginTransaction(),開始事務;endTransaction(),結束事務。除此之外,SQLiteDatabase還提供了一個inTransaction()方法用來判斷當前上下文是否處於事務環境中。當程序執行endTransaction()方法時將會結束事務,到底是回滾事務還是提交事務取決於SQLiteDatabase是否調用了setTransactionSuccessful()方法來設置事務標志,如果程序事務執行中調用該方法設置了事務成功則提交事務,否則程序將回滾事務。

  示例源碼下載

總結

  上面就基本講解了SQLite在Android中的時候,雖然有兩種操作方式,並且直接使用SQL語句操作數據庫對於熟悉SQL語句的開發者開說,是非常貼心的,但是在Android中,還是有必要了解一下使用SQLiteDatabase提供的方法操作數據庫的方式,因為Android有一個內容提供者(Content Provider),可以使用外部應用訪問內部應用的數據,它傳遞數據的形式,大部分是與SQLiteDatabase內置方法的參數一致的,所以如果同一使用SQLiteDatabase提供的方法操作數據庫,是很方便的,無需額外轉換SQL語句。

 

SQLiteExpertSetup下載地址:part1part2

  請支持原創,尊重原創,轉載請注明出處。謝謝。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM