android菜鳥學習筆記20----Android數據存儲(四))Android數據庫操作


Android內置了一個名為SQLite的關系型數據庫,這是一款輕量型的數據庫,操作十分簡便。SQLite與別的數據庫不同的是,它沒有數據類型。可以保存任何類型的數據到你所想要保存的任何表的任何列中。但它又支持常見的類型比如: NULL, VARCHAR, TEXT, INTEGER, BLOB, CLOB...等。

唯一的例外是:integer primary key 此字段只能存儲64位整數。

在JAVA項目中,要使用JDBC操作數據庫需要加載數據庫驅動,連接數據庫等操作。Android簡化了我們的數據庫操作,無需由我們進行數據庫驅動加載、連接等操作。

Android中進行數據庫操作,需要涉及到如下幾個類:

1)SQLiteOpenHelper:

在android.database.sqlite包下,這是一個抽象的幫助類,用於管理數據庫的創建及版本維護操作。

我們在需要獲取該類的對象時,需要自定義類繼承自SQLiteOpenHelper類,並實現其中的onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int),可以選擇性地實現 onOpen(SQLiteDatabase)。這個類會自動幫助我們在需要時打開數據庫,在不存在時創建數據庫,在必要時更新數據庫。

常用方法:

 

一般,在自定義的子類中調用父類中第一個構造方法即可。

構造函數的參數說明:

context:應用的上下文對象

name:要操作的數據庫的名稱

factory:cursor工廠類對象,一般指定為null

version:數據庫的版本號,必須大於等於1,由於控制數據庫的升級。

 

注意到,只有onCreate()和onUpgrade()是抽象方法,所以自定義子類繼承SQLiteOpenHelper時,一定要實現這兩個方法。

其中:

onCreate()方法會在數據庫不存在,第一次創建時調用,所以數據庫中的初始化操作,如創建表等操作需要在該方法中完成。

onUpgrade()方法,新的版本號比原來有提升時,調用,用以完成數據庫的升級操作,如新版本的app中,需要添加一張表,或者修改某個表,就需要在新版本的app創建SQLiteOpenHelper對象時,向其構造函數傳入一個更大的版本號,這個版本號會被newVersion接收。

 

getReadaleDatabase()方法,創建或打開一個數據庫,返回代表該數據庫的只讀的SQLiteDatabase對象

getWritableDatabase()方法,創建或打開一個數據庫,返回代表該數據庫的可讀可寫的SQLiteDatabase對象。

 

close()方法,用於關閉打開的數據庫對象。

2)SQLiteDatabase:

通過SQLiteOpenHelper對象獲取SQLiteDatabase對象后,便可以調用SQLiteDatabase類的相關方法進行數據庫的增刪改查操作了。

該類的常用方法有:

 

execSQL()方法用於執行SQL語句,可以用於執行不需要返回值的一些數據庫操作。

 

rawQuery()方法一般被用於執行需要返回值的查詢操作,查詢的結果保存在Cursor對象中。

除了直接執行SQL語句進行數據庫操作的方法之外,該類還封裝幾個更易用的增刪改查方法。

 

insert()方法,用於向數據庫中插入數據,參數說明:

table:指定要插入數據的表明

nullColumnHack:一般指定為null即可

注意:當values也為null,表示想向數據庫中插入一條空記錄時,該方法實際執行的SQL語句為insert into table(null) values(null);這樣一條sql語句是沒法執行的,所以,需要將nullColumnHack指定為任意一個可以為空的字段的字段名。如:nullColumnHack指定為”name”,values為null,此時SQL語句為insert into table(name) values(null);則可以正常執行了。

values:是一個ContentValues對象,用於存放要插入的各個字段名與值的對應關系。

 

delete()方法,用於刪除表中的記錄,參數說明:

table指定要操作的表名

whereClause指定where字句,可以包含占位符”?”,如“name = ?”,實際執行時,占位符會被第三個參數中對應索引的值替換

whereArgs:指定where字句中,占位符對應的值,如new String[]{“zhangsan”}

實際執行的語句就是delete from table where name = “zhangsan”

 

update()方法,用於執行表中記錄的更新操作,參數說明:

table,whereClause,whereArgs與delete相同含義

values用於指定對應字段名要更新的值的映射關系。

 

query()記錄查詢方法,最簡單的一個重載形式也有七個參數,都是select中的各個字句部分,如where子句部分,group by子句部分,having,order by等。參數說明:

table指定要查詢的表名

colmns指定要查詢的字段

selection:可以帶占位符的where子句部分

slectionArgs:where子句占位符對應的值

groupBy:group by子句部分

having:having子句部分

orderBy:order by 子句部分

等等

 

isOpen()判斷數據庫是否已打開。

 

beginTransaction()用於開啟事務操作

 

setTransactionSuccessful()方法用於標記事務操作成功

 

endTransaction()方法用於關閉事務,若事務標記成功,則提交事務操作,否則,則回滾失敗的事務操作。

其他方法,在需要時,可以查詢API幫助手冊。

3)Cursor:

Cursor是一個接口,其實現類用於存放SQL語句查詢的結果集。SQLiteDatabase的rawQuery()及query()方法均會返回該接口的對象,用以存儲操作查詢返回的結果集。

Cursor對象維持一個游標,默認指向結果集第一條記錄之前的位置,可以通過下面幾個方法,來移動游標,從而取得需要的記錄。當游標已在第一條記錄之前,調用moveToPreious(),或者已指向最后一條記錄,調用moveToNext()方法時,均會返回false()標識移動失敗。

 

 

getShort()、getString()、getInt()等getXXX()方法,用於根據當前記錄的字段的索引獲取字段的值。

 

getColumnName()用於根據索引獲取字段名

getColumnNames()用於獲取所有的字段名,其順序與在Cursor中保存的順序相同。

getCount()用於獲取當前Cursor對象中保存的記錄數。

 

getColumnCount()方法用於獲取記錄的字段總數

getColumnIndex()用於根據字段名獲取其索引,不存在時返回-1。

 

close()方法,用於關閉當前Cursor對象。

4)ContentValues:

該類用於存放鍵值對的數據,在數據庫的插入更新等操作中,可以使用字段名作為鍵,使用要插入或更新的字段值作為值。

常用的是put()方法,向ContentValues對象中存儲數據。

如:put(“name”,”zhangsan”);等

以上便是數據庫操作所涉及的幾個主要的類和接口。

下面通過一個具體的代碼,來學習使用這些個API:

第一步:

自定義類繼承SQLiteOpenHelper類,實現onCreate()和onUpgrade()方法:

 1 public class MySqliteHelper extends SQLiteOpenHelper {
 2 
 3       public static final String TAG = "MYSQLITEHELPER";
 4 
 5       public static final String CREATE_STUDENT = "create table t_student (" +
 6 
 7                  "id integer primary key, name varchar(20), gender varchar(10), age integer)";
 8 
 9       public MySqliteHelper(Context context, String name, CursorFactory factory,
10 
11                  int version) {
12 
13            super(context, name, factory, version);
14 
15       }
16 
17       @Override
18 
19       public void onOpen(SQLiteDatabase db) {
20 
21            Log.i(TAG,"open db");
22 
23            super.onOpen(db);
24 
25       }
26 
27       @Override
28 
29       public void onCreate(SQLiteDatabase db) {
30 
31            Log.i(TAG,"create db");
32 
33            Log.i(TAG,"before excSql");
34 
35            db.execSQL(CREATE_STUDENT);
36 
37            Log.i(TAG,"after excSql");
38 
39       }
40 
41       @Override
42 
43       public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
44 
45 
46       }
47 
48 }

第二步,定義實體類:

 1 package cn.csc.sqlite.bean;
 2 
 3  
 4 
 5 public class Student {
 6 
 7       private int id;
 8 
 9       private String name;
10 
11       private String gender;
12 
13       private int age;
14 
15  
16 
17       public int getId() {
18 
19            return id;
20 
21       }
22 
23       public void setId(int id) {
24 
25            this.id = id;
26 
27       }
28 
29       public String getName() {
30 
31            return name;
32 
33       }
34 
35       public void setName(String name) {
36 
37            this.name = name;
38 
39       }
40 
41       public String getGender() {
42 
43            return gender;
44 
45       }
46 
47       public void setGender(String gender) {
48 
49            this.gender = gender;
50 
51       }
52 
53       public int getAge() {
54 
55            return age;
56 
57       }
58 
59       public void setAge(int age) {
60 
61            this.age = age;
62 
63       }
64 
65      
66 
67       public Student() {
68 
69            super();
70 
71       }
72 
73       public Student(int id, String name, String gender, int age) {
74 
75            super();
76 
77            this.id = id;
78 
79            this.name = name;
80 
81            this.gender = gender;
82 
83            this.age = age;
84 
85       }
86 
87       @Override
88 
89       public String toString() {
90 
91            return "Student [id=" + id + ", name=" + name + ", gender=" + gender
92 
93                       + ", age=" + age + "]";
94 
95       }
96 
97 }

第三步,定義數據庫操作類:

 1 public class StudentDao {
 2 
 3       private SQLiteOpenHelper helper;
 4 
 5       private Context context;
 6 
 7       public StudentDao(Context context){
 8 
 9            this.context = context;
10 
11       }
12 
13       public void insert(Student stu){
14 
15            helper = new MySqliteHelper(context,"students.db", null, 1);
16 
17            Log.i("MYSQLITEHELPER","before get db");
18 
19            SQLiteDatabase db = helper.getWritableDatabase();
20 
21            Log.i("MYSQLITEHELPER","after get db");
22 
23            db.execSQL("insert into t_student(name, gender, age) values(?,?,?)" , new Object[]{stu.getName(),stu.getGender(),stu.getAge()});
24 
25            db.close();
26 
27       }
28 
29 }

第四步,定義測試類,測試StudentDao的insert()方法:

 1 public class TestDao extends AndroidTestCase {
 2 
 3       public void testInsert(){
 4 
 5            StudentDao dao = new StudentDao(getContext());
 6 
 7            dao.insert(new Student(0,"zhangsan", "male", 23));
 8 
 9       }
10 
11 }

運行該測試方法,運行結果如下:

 

注意到,定義SQLiteOpenHelper對象,並不會創建數據庫,只有調用getWritableDatabase()或者getReadableDatabase()才會調用onCreate()方法,onCreate()內部執行了execSQL()方法,但是並沒有調用onOpen()方法。onCreate()方法執行完成后,才接着調用了onOpen()方法。

 

從File Explorer中可以看出,students.db存放在/data/data/應用包名/databases/下。

再次運行,由於數據庫已經存在,onCreate()方法就沒再被調用。

可以通過sqlite3命令來查看數據庫內容:

 

sqlite的簡單使用說明:

adb shell掛載虛擬機控制台

cd /data/data/cn.csc.sqlite/databases進入數據庫文件所在目錄

sqlite3 數據庫文件名: 管理數據庫文件名所指定的數據庫,如sqlite3  students.db,然后光標變為sqlite>,表示進入sqlite操作模式。

.tables 查看數據庫中所有的表

可以直接輸入sql語句並執行

.exit 退出sqlite操作模式

.schema 查看查看庫中所有表的DDL語句

.mode list|column|insert|line|tabs|tcl|csv 改變輸出格式

 

若覺得命令行用着不習慣,可以從File Explorer中把數據庫文件導出到電腦中,然后使用圖形化工具,如sqlite expert來查看管理數據庫。

下載地址:http://pan.baidu.com/s/1jG8G7QY

StudentDao中添加getAllStudents()方法:

 1 public List<Student> getAllStudents(){
 2 
 3            List<Student> list = new ArrayList<Student>();
 4 
 5            helper = new MySqliteHelper(context,"students.db", null, 1);
 6 
 7            SQLiteDatabase db = helper.getWritableDatabase();
 8 
 9            Cursor cursor = db.rawQuery("select id,name,gender,age from t_student", null);
10 
11            if(cursor == null){
12 
13                  return null;
14 
15            }
16 
17            while(cursor.moveToNext()){
18 
19                  Student stu = new Student(cursor.getInt(0),cursor.getString(1),cursor.getString(2),cursor.getInt(3));
20 
21                  Log.i("MYSQLITEHELPER",stu.toString());
22 
23                  list.add(stu);
24 
25            }
26 
27           
28 
29            return list;
30 
31 }

測試,輸出:

 

上面用的都是直接寫完整的SQL語句,下面添加幾個方法,調用SQLiteDatabase封裝的幾個簡單操作的API:

update()方法的使用

StudentDao類添加:修改指定id的學生的姓名:

 1 public void updateNameById(int id, String newName){
 2 
 3            helper = new MySqliteHelper(context,"students.db", null, 1);
 4 
 5            SQLiteDatabase db = helper.getWritableDatabase();
 6 
 7            ContentValues values = new ContentValues();
 8 
 9            values.put("name", newName);
10 
11            db.update("t_student", values, "id=?", new String[]{id+""});
12 
13 }

測試代碼:

1 public void testUpdate(){
2 
3            StudentDao dao = new StudentDao(getContext());
4 
5            dao.updateNameById(1, "dqrcsc");
6 
7 }

運行前后:

 

delete()方法的使用

StudentDao類添加:刪除指定id的學生

1 public void deleteById(int id){
2 
3            helper = new MySqliteHelper(context,"students.db", null, 1);
4 
5            SQLiteDatabase db = helper.getWritableDatabase();
6 
7            db.delete("t_student", "id=?", new String[]{id+""});
8 
9 }

 

測試代碼:

1 public void testDelete(){
2 
3            StudentDao dao = new StudentDao(getContext());
4 
5            dao.deleteById(2);
6 
7 }

 運行前后:

 

insert()方法的使用

StudentDao類中添加如下方法:

 1 public void addStudent(Student stu){
 2 
 3            helper = new MySqliteHelper(context,"students.db", null, 1);
 4 
 5            SQLiteDatabase db = helper.getWritableDatabase();
 6 
 7            ContentValues values = new ContentValues();
 8 
 9            values.put("name", stu.getName());
10 
11            values.put("gender", stu.getGender());
12 
13            values.put("age", stu.getAge());
14 
15            db.insert("t_student", null, values);
16 
17 }

測試代碼:

1 public void testAddStudent(){
2 
3            StudentDao dao = new StudentDao(getContext());
4 
5            dao.addStudent(new Student(0,"csc","male",24));
6 
7 }

運行前后:

 

query()方法的使用

 1 public Student getStudentById(int id){
 2 
 3            Student stu = null;
 4 
 5            helper = new MySqliteHelper(context,"students.db", null, 1);
 6 
 7            SQLiteDatabase db = helper.getWritableDatabase();
 8 
 9            Cursor cursor = db.query("t_student", new String[]{"id","name","gender","age"}, "id=?", new String[]{id+""}, null, null, null);
10 
11            if(cursor == null){
12 
13                  return null;
14 
15            }
16 
17            if(cursor.moveToFirst()){
18 
19                  stu = new Student(cursor.getInt(0),cursor.getString(1),cursor.getString(2),cursor.getInt(3));
20 
21            }
22 
23            return stu;
24 
25 }

 

測試代碼:

1 public void testGetStudentById(){
2 
3            StudentDao dao = new StudentDao(getContext());
4 
5            Student stu = dao.getStudentById(1);
6 
7            Log.i("MYSQLITEHELPER",stu.toString());
8 
9 }

輸出:

 

 關於事務的操作

被用爛來的例子,就是銀行轉賬問題,一個賬戶轉出,一個賬戶轉入,兩個操作要么同時成功,要么同時失敗。

這里懶得再建一張表了,就轉年齡吧,其實換湯不換葯,原理完全一樣。

假設年齡可以在學生之間轉換,我要把自己的年齡轉10歲給lisi這個同學,要保證這整個操作的原子性,就需要用到事務。

在StudentDao中添加轉賬年齡的方法:

 1 public void transAge(){
 2 
 3            helper = new MySqliteHelper(context,"students.db", null, 1);
 4 
 5            SQLiteDatabase db = helper.getWritableDatabase();
 6 
 7            db.execSQL("update t_student set age = age - 10 where name = ?", new String[]{"dqrcsc"});
 8 
 9            int i = 1/0; 10 
11            db.execSQL("update t_student set age = age + 10 where name = ?", new String[]{"lisi"});
12 
13 }

注意兩條update語句之間有個1/0的操作,會導致程序異常終止,只有第一條被執行。

執行前后:

 

可以看到,我的年齡減去10歲,而lisi的年齡並沒有加上10歲。

修改代碼,改用事務處理:

 1 public void transAge(){
 2 
 3            helper = new MySqliteHelper(context,"students.db", null, 1);
 4 
 5            SQLiteDatabase db = helper.getWritableDatabase();
 6 
 7            db.beginTransaction();
 8 
 9            try{
10 
11                  db.execSQL("update t_student set age = age - 10 where name = ?", new String[]{"dqrcsc"});
12 
13                  int i = 1/0;
14 
15                  db.execSQL("update t_student set age = age + 10 where name = ?", new String[]{"lisi"});
16 
17                  db.setTransactionSuccessful();
18 
19            }finally{
20 
21                  db.endTransaction();
22 
23            }
24 
25 }

這一次,我的年齡沒有減少,lisi的年齡也沒有增加,保證了一致性。

 

關於更新數據庫版本的簡單示例

如,由於業務需要,更新了APP,新版本的APP在數據庫中增加了一張教師表,現在修改onCreate()方法:

 1 public static final String CREATE_TEACHER = "create table t_teacher(id integer primary key, name varchar(20))";
 2 
 3 public void onCreate(SQLiteDatabase db) {
 4 
 5            // TODO Auto-generated method stub
 6 
 7            db.execSQL(CREATE_STUDENT);
 8 
 9            db.execSQL(CREATE_TEACHER);
10 
11 }

運行,發現根本沒有增加t_teacher這張表,因為數據庫已存在,onCreate()方法不會被運行,這時,就需要用到onUpgrade()方法了:

 1 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 2 
 3            // TODO Auto-generated method stub
 4 
 5            if(oldVersion == 1 && newVersion == 2){
 6 
 7                  db.execSQL(CREATE_TEACHER);
 8 
 9            }
10 
11 }

在StudentDao中添加addTeacher()方法:

1 public void addTeacher(){
2 
3            helper = new MySqliteHelper(context,"students.db", null, 2);
4 
5            SQLiteDatabase db = helper.getWritableDatabase();
6 
7            db.execSQL("insert into t_teacher(name) values(?)",new String[]{"wanger"});
8 
9 }

注意,這里指定的版本號為2。

測試運行結果:

 

數據庫操作的學習,就簡單學到這里。

 


免責聲明!

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



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