不會數據庫的司機不是好廚師。今天抽了點時間對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循環就是一個事務,整體速度提升非常大。
以上就是主要的常見的數據庫操作,還有一些其它的操作就不細舉了。比如索引之類的。
本期節目就到這裏,感謝大家的收看,下期再見!