Android數據庫Sqlite完全解析

不會數據庫的司機不是好廚師。今天抽了點時間對Sqlite數據庫的使用做了個總結記錄。


老規矩,在節目開始之前,首先來一個搞笑段子:

未婚女發現自己懷孕後,第一反應是:“完了,我媽非弄死我不可。”殊不知,她肚子裏的孩子也在想:“完了,我媽非弄死我不可。。。”


Android中對數據庫的操作主要就是四個:增、刪、改、查,加上建表和更新。相比Oracle很多的DDL、DCL操作還是簡單很多的。

這篇文章使用User作爲例子,記錄一下增、刪、改、查的使用方法。


首先是Java Bean:User

public class User {

    private int id = 0;

    private String name = null;

    private int age = 0;

    public User(){}

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "[" + id + ", " + name + ", " + age + "]";
    }
}


然後是數據庫操作,就一個類:SqliteOpenHelper

我先把整個類的代碼貼出來,再一段段註釋。

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import java.util.ArrayList;
import java.util.List;


public class DataBaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "test.db";

    private static final int VERSION_CODE = 101;

    public static final String TABLE = "user";

    public static final String ID = "id";

    public static final String NAME = "name";

    public static final String AGE = "age";

    private SQLiteDatabase db;

    private static DataBaseHelper instance = null;

    public static DataBaseHelper getInstance(Context context) {
        if(instance == null) {
            instance = new DataBaseHelper(context);
        }
        return instance;
    }

    private DataBaseHelper(Context context) {
        super(context, DATABASE_NAME, null, VERSION_CODE);
        db = this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TABLE + " ("
                + ID + " INTEGER NOT NULL,"
                + NAME + " TEXT NOT NULL,"
                + AGE + " INTEGER NOT NULL);");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion) {
            db.execSQL("DROP TABLE " + TABLE + " IF EXISTS");
        }
    }

    public boolean insertUser(User user) {
        boolean result = false;
        if(user != null) {
            synchronized (db) {
                ContentValues values = new ContentValues();
                values.put(ID, user.getId());
                values.put(NAME, user.getName());
                values.put(AGE, user.getAge());
                long insertRowId = db.insert(TABLE, ID, values);
                result = insertRowId != -1;
            }
        }
        return result;
    }

    //批量插入
    public boolean insertUsers(List<User> users) {
        boolean result = true;
        if(users != null && users.size() > 0) {
            db.beginTransaction();
            for(User user : users) {
                boolean insert = insertUser(user);
                if (!insert) {
                    result = false;
                    break;
                }
            }
            db.setTransactionSuccessful();
            db.endTransaction();
        }
        return result;
    }

    //刪除user
    public boolean deleteUser(int id) {
        boolean result = false;
        if(id >= 0) {
            synchronized (db) {
                int deleteRows = db.delete(TABLE, ID + "=?", new String[]{String.valueOf(id)});
                result = deleteRows > 0;
            }
        }
        return result;
    }

    //更新user
    public boolean updateUser(User user) {
        boolean result = false;
        if(user != null) {
            synchronized (db) {
                ContentValues values = new ContentValues();
                values.put(NAME, user.getName());
                values.put(AGE, user.getAge());
                int updateRows = db.update(TABLE, values, ID, new String[]{String.valueOf(user.getId())});
                result = updateRows > 0;
            }
        }
        return result;
    }

    //查詢所有人
    public List<User> queryAllUsers() {
        List<User> users = new ArrayList<User>();
        synchronized (db) {
            Cursor cursor = db.query(TABLE, new String[]{ID, NAME, AGE}, null, null, null, null, String.valueOf(ID));
            if(cursor != null && cursor.getCount() > 0) {
                while(cursor.moveToNext()) {
                    User user = new User();
                    user.setId(cursor.getInt(cursor.getColumnIndex(ID)));
                    user.setName(cursor.getString(cursor.getColumnIndex(NAME)));
                    user.setAge(cursor.getInt(cursor.getColumnIndex(AGE)));
                    users.add(user);
                }
            }
            if (cursor != null) {
                cursor.close();
            }
        }
        return users;
    }

    //查詢年齡大於20歲的所有人
    public List<User> queryUsersByAge(int minAge) {
        List<User> users = new ArrayList<User>();
        if(minAge > 0) {
            synchronized (db) {
                Cursor cursor = db.query(TABLE, new String[]{ID, NAME, AGE}, ID + ">?", new String[]{String.valueOf(minAge)}, null, null, String.valueOf(ID));
                if(cursor != null && cursor.getCount() > 0) {
                    while(cursor.moveToNext()) {
                        User user = new User();
                        user.setId(cursor.getInt(cursor.getColumnIndex(ID)));
                        user.setName(cursor.getString(cursor.getColumnIndex(NAME)));
                        user.setAge(cursor.getInt(cursor.getColumnIndex(AGE)));
                        users.add(user);
                    }
                }
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        return users;
    }

    //分頁查詢:頁面上顯示User按年齡排序,每頁顯示10個,查詢第n頁的User
    public List<User> queryUsersByPage(int pageNumber) {
        List<User> users = new ArrayList<User>();
        if(pageNumber > 0) {
            synchronized (db) {
                String queryString = "select * from " + TABLE + " order by " + AGE + "limit 10 offset " + (10 * (pageNumber - 1));
                Cursor cursor = db.rawQuery(queryString, null);
                if(cursor != null && cursor.getCount() > 0) {
                    while(cursor.moveToNext()) {
                        User user = new User();
                        user.setId(cursor.getInt(cursor.getColumnIndex(ID)));
                        user.setName(cursor.getString(cursor.getColumnIndex(NAME)));
                        user.setAge(cursor.getInt(cursor.getColumnIndex(AGE)));
                        users.add(user);
                    }
                }
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        return users;
    }
}


1、常量

private static final String DATABASE_NAME = "test.db";

private static final int VERSION_CODE = 101;

public static final String TABLE = "user";

public static final String ID = "id";

public static final String NAME = "name";

public static final String AGE = "age";


分別是:

數據庫名(在文件系統中的文件名)

數據庫版本號(版本更新時有用)

表名(一個數據庫可以有多個表)

User Id(User的唯一標識,區別數據庫id)

姓名、年齡不解釋


2、構造函數

private DataBaseHelper(Context context) {
    super(context, DATABASE_NAME, null, VERSION_CODE);
    db = this.getWritableDatabase();
}

指定數據庫名和版本號,通過單例模式獲取。


3、建表

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE " + TABLE + " ("
            + ID + " INTEGER NOT NULL,"
            + NAME + " TEXT NOT NULL,"
            + AGE + " INTEGER NOT NULL);");
}

就是拼接String,調用db.execSQL執行。和在命令行敲SQL語句沒區別。


4、更新

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (newVersion > oldVersion) {
        db.execSQL("DROP TABLE " + TABLE + " IF EXISTS");
    }
}

爲什麼系統要提供這個方法呢,是因爲隨着App的更新,數據庫可能會增加新的字段,或者刪除字段,導致之前的表不能再使用。

所以需要更新表結構,或者刪除表,重新創建。

這裏就是判斷數據庫版本號有更新後,就刪除表,重新創建。


5、插入一條User

public boolean insertUser(User user) {
    boolean result = false;
    if(user != null) {
        synchronized (db) {
            ContentValues values = new ContentValues();
            values.put(ID, user.getId());
            values.put(NAME, user.getName());
            values.put(AGE, user.getAge());
            long insertRowId = db.insert(TABLE, ID, values);
            result = insertRowId != -1;
        }
    }
    return result;
}

通過ContentValues把user的屬性值保存起來(其實就是一個map),然後調用db.insert(TABLE, ID, values);插入。

第二個參數是什麼意思呢?

第二個參數叫做nullColumnHack,是用於當你的values爲空時,指定一個列插入空值,以此來保證插入操作不會失敗。

既然同樣是插入空值,那和values是空有什麼區別呢?這個問題要回到原始的SQL語句來理解。

Android調用系統Api實行數據庫操作,其實最終都是轉化成SQL語句去執行,我們可以想象一下,如果我們不添加nullColumnHack的話,那麼我們的sql語句最終的結果將會類似insert into user () values();這語句顯然是不允許的。而如果我們添加上nullColumnHack呢,sql將會變成這樣,insert into user (nullColumnHack) values(null);哎~這樣在語法上就是OK的了。


6、刪除一條

public boolean deleteUser(int id) {
    boolean result = false;
    if(id >= 0) {
        synchronized (db) {
            int deleteRows = db.delete(TABLE, ID + "=?", new String[]{String.valueOf(id)});
            result = deleteRows > 0;
        }
    }
    return result;
}

這個比較簡單,刪除表中指定id的一條,一目瞭然。


7、更新一條

public boolean updateUser(User user) {
    boolean result = false;
    if(user != null) {
        synchronized (db) {
            ContentValues values = new ContentValues();
            values.put(NAME, user.getName());
            values.put(AGE, user.getAge());
            int updateRows = db.update(TABLE, values, ID, new String[]{String.valueOf(user.getId())});
            result = updateRows > 0;
        }
    }
    return result;
}

這個也挺清晰的,就是指定一個id,找到一條記錄,然後更新這條記錄爲ContentValues的值。


8、查詢

public List<User> queryUsersByAge(int minAge) {
    List<User> users = new ArrayList<User>();
    if(minAge > 0) {
        synchronized (db) {
            Cursor cursor = db.query(TABLE, new String[]{ID, NAME, AGE}, ID + ">?", new String[]{String.valueOf(minAge)}, null, null, String.valueOf(ID));
            if(cursor != null && cursor.getCount() > 0) {
                while(cursor.moveToNext()) {
                    User user = new User();
                    user.setId(cursor.getInt(cursor.getColumnIndex(ID)));
                    user.setName(cursor.getString(cursor.getColumnIndex(NAME)));
                    user.setAge(cursor.getInt(cursor.getColumnIndex(AGE)));
                    users.add(user);
                }
            }
            if (cursor != null) {
                cursor.close();
            }
        }
    }
    return users;
}

代碼看起來比較多,但是主要的就db.query這句。

它有7個參數,分別是:表名、查詢的列數組、條件、條件參數數組(用於替換條件中的佔位符?)、groupby分組、having過濾、orderby排序。


9、分頁查詢

public List<User> queryUsersByPage(int pageNumber) {
    List<User> users = new ArrayList<User>();
    if(pageNumber > 0) {
        synchronized (db) {
            String queryString = "select * from " + TABLE + " order by " + AGE + "limit 10 offset " + (10 * (pageNumber - 1));
            Cursor cursor = db.rawQuery(queryString, null);
            if(cursor != null && cursor.getCount() > 0) {
                while(cursor.moveToNext()) {
                    User user = new User();
                    user.setId(cursor.getInt(cursor.getColumnIndex(ID)));
                    user.setName(cursor.getString(cursor.getColumnIndex(NAME)));
                    user.setAge(cursor.getInt(cursor.getColumnIndex(AGE)));
                    users.add(user);
                }
            }
            if (cursor != null) {
                cursor.close();
            }
        }
    }
    return users;
}

對比一下查詢,其實就是多了一個limit和offset

limit是指定一組查多少個,offset是指定跳過多少條記錄。

比如我們每組查詢10個,查詢第3組,那limit就是10 ,offset就是10 * (3 - 1)


10、批量插入

public boolean insertUsers(List<User> users) {
    boolean result = true;
    if(users != null && users.size() > 0) {
        db.beginTransaction();
        for(User user : users) {
            boolean insert = insertUser(user);
            if (!insert) {
                result = false;
                break;
            }
        }
        db.setTransactionSuccessful();
        db.endTransaction();
    }
    return result;
}

看上去和插入一條的差別就是for循環,但是有一個很重要的就是啓用了事物。

如果不用事務,每一次插入都是一次事務,耗時較大,for循環200條就就要好幾秒。

但是在for循環前db.beginTransaction();

在for循環後db.setTransactionSuccessful();db.endTransaction();

這樣,整個for循環就是一個事務,整體速度提升非常大。


以上就是主要的常見的數據庫操作,還有一些其它的操作就不細舉了。比如索引之類的。


本期節目就到這裏,感謝大家的收看,下期再見!















發佈了40 篇原創文章 · 獲贊 88 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章