自己動手寫Android資料庫框架
自己動手寫Android資料庫框架
相信不少開發者跟我一樣,每次都很煩惱自己寫資料庫,而且那些資料庫語句也經常記不住。當然網上也有很多很好的資料庫框架,你可以直接拿來用,但是 很多時候我們的專案,特別是一個小型的Andrond應用原本用到的資料庫結構比較簡單,沒必要去用那些有點臃腫的框架。當然,即使你用那些框架,當你遇到問題時,你是否也得去修改它?你要修改別人的框架必須的讀懂他人的設計程式碼。所以不管從那個角度出發,你都得掌握簡單的資料庫操作。那麼這篇部落格就從簡單的資料庫操作來學習Android資料庫相關知識點,然後一步一步去搭建自己的簡單型資料庫框架,以後就再也不用擔心害怕去寫資料庫了,直接拿自己的資料庫框架用就好了。
框架功能
public long insert(Object obj);插入資料
public List findAll(Class clazz);查詢所有資料
public List findByArgs(Class clazz, String select, String[] selectArgs) ;根據指定條件查詢滿足條件資料
public T findById(Class clazz, int id);根據id查詢一條記錄
public void deleteById(Class)
建立資料庫
Android系統中已經整合了Sqlite資料庫,我們直接使用它就好了,同時Android系統提供了一個資料庫幫助類SQLiteOpenHelper,該類是一個抽象類,所以得寫一個類來繼承它實現裡面的方法。程式碼如下:
MySQLiteHelper類
public class MySQLiteHelper extends SQLiteOpenHelper { public MySQLiteHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
當資料庫建立時系統會呼叫其中的 onCreate方法,那麼我們就可以來實現 onCreate 方法來建立資料庫表。假設我們要建立一張 Person表,表中有 id,name,age,flag欄位。那麼程式碼如下:
public class MySQLiteHelper extends SQLiteOpenHelper { public static final String CREATE_TABLE = "create table Person (" + "id integer primary key autoincrement, " + "name text, " + "age integer, " + "flag boolean)"; public MySQLiteHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE); } ... }
由此我們的資料庫幫助類就完成了,接下來是這麼使用的:
private static final String DB_NAME = "demo.db"; private static final int DB_VERSION = 1; public void oepnDB(){ MySQLiteHelper helper = new MySQLiteHelper(context, DB_NAME, null, DB_VERSION); SQLiteDatabase db = helper.getWritableDatabase(); }
有以上程式碼就已經完成了一個資料庫建立以及一張表的建立,是不不是感覺不是很難呢?這麼看起來的確不是很難,但是我的也不得不每次去繼承SQLiteOpenHelper類來實現裡面的方法。關鍵是每次都要去寫建立表語句
public static final String CREATE_TABLE = "create table Person (" + "id integer primary key autoincrement, " + "name text, " + "age integer, " + "flag boolean)";
這裡表的欄位只有4個,如果有一天你遇到表裡的欄位有10列怎麼辦?還繼續按照上面的方法寫建立表語句麼?你就不嫌繁瑣麼?而且容易粗錯。那麼有沒有超級簡單的方法一步完成表語句的建立呢?你細想:存放在資料庫中表的這些欄位無非就是一個Person類中的所有成員變數,這麼一來是否可以只透過Person型別直接建立表語句呢?答案是肯定的。我們透過java 的反射機制來一步一勞永逸的實現建表操作。程式碼如下:
/** * 得到建表語句 * * @param clazz 指定類 * @return sql語句 */ private String getCreateTableSql(Class> clazz) { StringBuilder sb = new StringBuilder(); //將類名作為表名 String tabName = Utils.getTableName(clazz); sb.append("create table ").append(tabName).append(" (id INTEGER PRIMARY KEY AUTOINCREMENT, "); //得到類中所有屬性物件陣列 Field[] fields = clazz.getDeclaredFields(); for (Field fd : fields) { String fieldName = fd.getName(); String fieldType = fd.getType().getName(); if (fieldName.equalsIgnoreCase("_id") || fieldName.equalsIgnoreCase("id")) { continue; } else { sb.append(fieldName).append(Utils.getColumnType(fieldType)).append(", "); } } int len = sb.length(); sb.replace(len - 2, len, ")"); Log.d(TAG, "the result is " + sb.toString()); return sb.toString(); }
工具類程式碼如下:
package com.xjp.databasedemo; import android.text.TextUtils; import java.util.Locale;/** * Created by xjp on 2016/1/23. */public class DBUtils { //得到每一列欄位的資料型別 public static String getColumnType(String type) { String value = null; if (type.contains("String")) { value = " text "; } else if (type.contains("int")) { value = " integer "; } else if (type.contains("boolean")) { value = " boolean "; } else if (type.contains("float")) { value = " float "; } else if (type.contains("double")) { value = " double "; } else if (type.contains("char")) { value = " varchar "; } else if (type.contains("long")) { value = " long "; } return value; } //得到表名 public static String getTableName(Class> clazz){ return clazz.getSimpleName(); } public static String capitalize(String string) { if (!TextUtils.isEmpty(string)) { return string.substring(0, 1).toUpperCase(Locale.US) + string.substring(1); } return string == null ? null : ""; } }
如此一來,使用者建立資料庫表就變的很簡單了,傳入Person類的型別(Person.class)作為引數,那麼程式碼就幫你建立出了一張名字為Person的表。使用程式碼如下:
class MySqLiteHelper extends SQLiteOpenHelper { .................. @Override public void onCreate(SQLiteDatabase db) { createTable(db); } /** * 根據制定類名建立表 */ private void createTable(SQLiteDatabase db) { db.execSQL(getCreateTableSql(Person.class)); .............. }
是不是很簡單!!!領導再也不用擔心我不會建立資料庫了。
資料庫操作–插入
android提供的資料庫插入操作
對資料庫插入操作 SQLite提供瞭如下方法
public long insert(String table, String nullColumnHack, ContentValues values)
可以看到,第一個引數是table 表示表名,第二個引數通常用不到,傳入null即可,第三個引數將資料以 ContentValues鍵值對的形式儲存。比如我們在資料庫中插入一條人Person的資訊程式碼如下:
public void insert(Person person){ ContentValues values = new ContentValues(); values.put("name",person.getName()); values.put("age",person.getAge()); values.put("flag",person.getFlag()); db.insert("Person",null,values); }
其中ContentValues是以鍵值對的形式儲存資料,上面程式碼中的key 分別對應資料庫中的每一列的欄位,vaule分別對應著該列的值。你是否發現Person類中有幾個屬性就得寫多少行values.put(key,value);加入它有10個欄位需要儲存到資料庫中,你是否覺得這樣很麻煩呢?覺得麻煩就對了,接下來我們利用反射來一步完成以上資料庫插入操作。
資料庫插入框架
資料庫插入操作框架可以減輕你寫程式碼量,讓你一步完成資料庫插入操作而無須關注其內部繁瑣的操作。同樣利用java反射來實現以上效果。程式碼如下:
/** * 插入一條資料 * * @param obj * @return 返回-1代表插入資料庫失敗,否則成功 * @throws IllegalAccessException */ public long insert(Object obj) { Class> modeClass = obj.getClass(); Field[] fields = modeClass.getDeclaredFields(); ContentValues values = new ContentValues(); for (Field fd : fields) { fd.setAccessible(true); String fieldName = fd.getName(); //剔除主鍵id值得儲存,由於框架預設設定id為主鍵自動增長 if (fieldName.equalsIgnoreCase("id") || fieldName.equalsIgnoreCase("_id")) { continue; } putValues(values, fd, obj); } return db.insert(DBUtils.getTableName(modeClass), null, values); } ............ /** * put value to ContentValues for Database * * @param values ContentValues object * @param fd the Field * @param obj the value */ private void putValues(ContentValues values, Field fd, Object obj) { Class> clazz = values.getClass(); try { Object[] parameters = new Object[]{fd.getName(), fd.get(obj)}; Class>[] parameterTypes = getParameterTypes(fd, fd.get(obj), parameters); Method method = clazz.getDeclaredMethod("put", parameterTypes); method.setAccessible(true); method.invoke(values, parameters); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
有以上框架之後,我們現在來向資料庫插入一條資料程式碼如下:
Person person = new Person("Tom",18,false); DBManager.insert(person);
哇!如此簡單,一行程式碼解決繁瑣的插入操作。我們只需要傳入Person物件的例項作為引數即可完成資料庫插入操作。再也不用去構建什麼ContentVaules鍵值對了。
資料庫操作–查詢
android提供的資料庫查詢
android 的sqlite資料庫提供的查詢語句有rawQuery()方法。該方法的定義如下:
public Cursor rawQuery(String sql, String[] selectionArgs)
其中第一個引數是sql字串,第二個引數是用於替換SQL語句中佔位符(?)的字串陣列。返回結果存放在Cursor物件當中,我們只要迴圈一一取出資料即可。當然我們平時不怎麼用這個方法,因為需要記住很多資料庫查詢語句的規則等。Android給開發者封裝了另外一個資料庫查詢方法,即SQLiteDatabase中的query()方法。該方法的定義如下:
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
其中,
第一個引數是需要查詢資料的表名稱;
第二個引數指查詢表中的那幾列欄位,如果不指定則預設查詢所有列;
第三個引數是sql語句,表示查詢條件;
第四個引數是用於替換第三個引數sql語句中的佔位符(?)陣列,如果第三,四個引數不指定則預設查詢所有行;
第五個引數用於指定需要去group by的列,不指定則表示不對查詢結果進行group by操作。
第六個引數用於對group by之後的資料進行進一步的過濾,不指定則表示不進行過濾。
第七個引數用於指定查詢結果的排序方式,不指定則表示使用預設的排序方式。
query()方法的引數是不是很多,一般人都很難記住這些引數的意思,在用的時候就很不方便,比如你要查詢資料庫中 age=18的人,你的程式碼得這麼寫:
Cursor cursor = db.query("Person", null, "age = ?", new String[]{"18"}, null, null, null);
第三個引數是查詢條件,去約束查詢結果 age = 18,所以 第三個引數是“age= ?”,第四個引數用於替換第三個引數的佔位符(?),因此是String的陣列。查詢的結果儲存在Cursor中,為了拿到查詢結果,我們不得不去變數裡Cursor一一取出其中的資料並儲存。程式碼如下:
Listlist = new ArrayList(); if (cursor != null && cursor.moveToFirst()) { do { Person person = new Person(); int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); String age = cursor.getString(cursor.getColumnIndex("age")); boolean flag = cursor.getInt(cursor.getColumnIndex("flag")) == 1 ? true : false; person.setId(id); person.setName(name); person.setAge(age); person.setFlag(flag); list.add(person); } while (cursor.moveToNext()); }
為了取得Cursor中的查詢結果,我們寫了如此多的繁瑣的程式碼,如果此時有一個新的Student類,那麼你是否又要去修改這個查詢方法呢?如此看來該查詢方法和取得結果是不是沒有通用性,很不方便使用。對於討厭敲重複程式碼的來說這樣很麻煩,用的不爽,那麼有沒有一種方法直接將查詢結果轉換成我需要的類的集合呢?這裡我們又要用到自己寫的查詢框架了,利用該框架一行程式碼即可搞定所有。
資料庫查詢框架
1.查詢資料庫中所有資料
/** * 查詢資料庫中所有的資料 * * @param clazz * @param以 List的形式返回資料庫中所有資料 * @return 返回list集合 * @throws IllegalAccessException * @throws InstantiationException * @throws NoSuchMethodException * @throws InvocationTargetException */ public List findAll(Class clazz) { Cursor cursor = db.query(clazz.getSimpleName(), null, null, null, null, null, null); return getEntity(cursor, clazz); } ..................... /** * 從資料庫得到實體類 * * @param cursor * @param clazz * @param * @return */ private List getEntity(Cursor cursor, Class clazz) { List list = new ArrayList(); try { if (cursor != null && cursor.moveToFirst()) { do { Field[] fields = clazz.getDeclaredFields(); T modeClass = clazz.newInstance(); for (Field field : fields) { Class> cursorClass = cursor.getClass(); String columnMethodName = getColumnMethodName(field.getType()); Method cursorMethod = cursorClass.getMethod(columnMethodName, int.class); Object value = cursorMethod.invoke(cursor, cursor.getColumnIndex(field.getName())); if (field.getType() == boolean.class || field.getType() == Boolean.class) { if ("0".equals(String.valueOf(value))) { value = false; } else if ("1".equals(String.valueOf(value))) { value = true; } } else if (field.getType() == char.class || field.getType() == Character.class) { value = ((String) value).charAt(0); } else if (field.getType() == Date.class) { long date = (Long) value; if (date 查詢所有資料並且自動儲存在List中返回,無須使用者去將Cursor解析成物件封裝。簡單易用,自需要一個方法一個引數即可。呼叫程式碼如下:
Listlist = dbManager.findAll(Person.class); 超級簡單啊!
2.查詢指定條件的資料
/** * 根據指定條件返回滿足條件的記錄 * * @param clazz 類 * @param select 條件語句 :("id>?") * @param selectArgs 條件(new String[]{"0"}) 查詢id=0的記錄 * @param型別 * @return 返回滿足條件的list集合 */ public List findByArgs(Class clazz, String select, String[] selectArgs) { Cursor cursor = db.query(clazz.getSimpleName(), null, select, selectArgs, null, null, null); return getEntity(cursor, clazz); } 3.根據指定id查詢一條資料
/** * 透過id查詢制定資料 * * @param clazz 指定類 * @param id 條件id * @param型別 * @return 返回滿足條件的物件 */ public T findById(Class clazz, int id) { Cursor cursor = db.query(clazz.getSimpleName(), null, "id=" + id, null, null, null, null); List list = getEntity(cursor, clazz); return list.get(0); } 使用者程式碼呼叫如下:
Person p = dbManager.findById(Person.class, 1);查詢id=1的資料,第一個引數為Person型別,第二個引數為id值,查詢結果直接儲存在Person物件p裡。
以上就是自己封裝的資料庫查詢操作,簡單易用,無須記住quary()方法中的那麼多引數,也無須自己去一個個解析Cursor資料並儲存。該方法一步到位,直接返回Person型別的list集合。註釋:其中用到的一些方法我暫時沒有貼出來,文章最後我會把例子和程式碼都貼出來。
資料庫刪除操作
android提供的刪除
android系統提供了sqlite資料庫刪除方法 delete(),其定義如下:
public int delete(String table, String whereClause, String[] whereArgs)其中,第一個參數列示表名,第二個引數是條件SQL語句,第三個引數是替換第二個引數中的佔位符(?)。假如我要刪除Person表中的age=18的資料,則程式碼呼叫如下:
db.delete("Person","age = ?",new String[]{"18"});資料庫刪除框架
刪除這一塊比較簡單,我直接貼出程式碼來
/** * 刪除記錄一條記錄 * * @param clazz 需要刪除的類名 * @param id 需要刪除的 id索引 */ public void deleteById(Class> clazz, long id) { db.delete(DBUtils.getTableName(clazz), "id=" + id, null); }使用者呼叫如下:
dbManager.deleteById(Person.class, 1);第一個 引數是Person類的型別,第二個引數是被刪除資料的id。是不是很簡單呢?它的實現如下:
/** * 刪除記錄一條記錄 * * @param clazz 需要刪除的類名 * @param id 需要刪除的 id索引 */ public void deleteById(Class> clazz, long id) { db.delete(DBUtils.getTableName(clazz), "id=" + id, null); }資料庫更新操作
android提供的更新操作
在android的sqlite中提供了update()方法來更新資料操作,其定義如下:
public int update(String table, ContentValues values, String whereClause, String[] whereArgs)update()方法接收四個引數,第一個引數是表名,第二個引數是一個封裝了待修改資料的ContentValues物件,第三和第四個引數用於指定修改哪些行,對應了SQL語句中的where部分。比如我要修改id=1的Person人的年齡age改成20,那麼程式碼實現如下:
ContentValues values = new ContentValues(); values.put("age",20); db.update("Person",values,"id = ?",new String[]{"1"});該方法也算比較簡單,那麼我們來看看自己寫的資料庫框架是怎麼實現的呢?
資料庫框架更新操作
ContentValues values = new ContentValues(); values.put("age", 34); dbManager.updateById(Person.class, values, 1);第一個引數為Person類的型別,第二個引數為需要更新的vaules,第三個引數是條件,更新id為1的資料。用法很簡單,它的實現如下:
/** * 更新一條記錄 * * @param clazz 類 * @param values 更新物件 * @param id 更新id索引 */ public void updateById(Class> clazz, ContentValues values, long id) { db.update(clazz.getSimpleName(), values, "id=" + id, null); }自此,資料庫的基本操作都羅列出來了,也說明了Android提供的sqlite資料庫在平時開發中的一些繁瑣的地方,所以自己總結提取了一個簡單型的資料庫操作框架,僅僅是比較簡單的操作,如果你有資料量大的操作,請出門左轉利用其他多功能成熟穩定的資料庫開源框架。該框架只適合資料量小,不存在表與表之間的對應關係,可以將查詢結果直接轉換成物件的輕量級框架。
原始碼以及示例地址:
原文連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2144/viewspace-2805371/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 自己動手寫一個持久層框架框架
- 自己動手寫個 Android客戶端Android客戶端
- 手擼一個簡易Android資料庫框架Android資料庫框架
- 手寫自己的MyBatis框架-SqlSessionMyBatis框架SQLSession
- 自己動手寫PromisePromise
- 自己動手實現Android中的三級快取框架Android快取框架
- 模仿vue自己動手寫響應式框架(三) - dom解析Vue框架
- 自己動手寫Impala UDF
- android 檢視資料庫和shaedpreference資料框架Android資料庫框架
- 自己手寫一個SpringMVC框架(簡化)SpringMVC框架
- 模仿vue自己動手寫響應式框架(二) - Vue物件建立Vue框架物件
- Android精通之OrmLite資料庫框架,Picasso框架,Okio框架,OKHttp框架AndroidORM資料庫框架HTTP
- 模仿vue自己動手寫響應式框架(四) - Vue物件構建Vue框架物件
- 手寫資料庫連線池資料庫
- 自己動手寫SQL執行引擎SQL
- 使用scrapy框架把資料非同步寫入資料庫框架非同步資料庫
- 核心技術靠化緣是要不來的——自己動手寫ORM框架ORM框架
- 【眼見為實】自己動手實踐理解資料庫READ COMMITTED && MVCC資料庫MITMVC
- 自己動手實現一個EventBus框架框架
- 自己動手寫basic直譯器 一
- 自己動手寫事件匯流排(EventBus)事件
- 自己動手寫Vector【Cherno C++教程】C++
- 基於Netty自己動手實現Web框架NettyWeb框架
- 從頭開始,手寫android應用框架(一)Android框架
- 模仿vue自己動手寫響應式框架(五)終章 - v-for指令實現Vue框架
- android資料庫如何進行版本升級?架構之資料庫框架升級Android資料庫架構框架
- 《四 資料庫連線池原始碼》手寫資料庫連線池資料庫原始碼
- WPF啟動流程-自己手寫Main函式AI函式
- Python3 動手自己寫谷歌翻譯Python谷歌
- 手寫一個業務資料比對庫
- 手寫Android事件匯流排框架Eventbus(簡易版)Android事件框架
- 【眼見為實】自己動手實踐理解資料庫REPEATABLE READ && Next-Key Lock資料庫
- mongodb怎麼手動建立資料庫MongoDB資料庫
- 淺析MyBatis(二):手寫一個自己的MyBatis簡單框架MyBatis框架
- Rxjava深入理解之自己動手編寫RxjavaRxJava
- 如果你想寫自己的Benchmark框架框架
- 手寫RPC框架RPC框架
- 手寫Spring框架Spring框架