手寫資料庫框架
阿新 • • 發佈:2018-12-09
目錄
android發展至今開源框架很多,資料庫框架也有一些比較好的,而對於我們開發者而言網上有很多框架,我們只需要拿過來會用,一般都可以解決專案中遇到的問題,而我們自己寫的程式碼量就比較少了,也很難突破開發瓶頸,開發模式變成了Ctrl+C Ctrl+V,而依賴過來的jar包,有些未必是專案需要用到的,很多冗餘程式碼量較大的時候,間接的導致app變大,所以這個時候就需要我們手寫程式碼來解決這一問題,今天寫一下資料庫框架。
正文部分
1.首先新建一個AS專案工程,建立一個頂層資料庫介面IBaseDao 這裡定義一些需要用的方法,最基礎的增刪改查之類的,讓實現類去呼叫,insert方法是需要插入的物件,當然我們需要插入的物件應該是由使用者去決定,所以這裡用範型,使用者傳入插入物件即可
public interface IBaseDao<T>{
/** 插入操作 */
long insert(T entity);
/** 刪除操作 */
int delete(String condition,String[] value);
}
2.再建立一個實現類去實現上面定義的介面,而我們需要操作資料庫,這裡就會用到android提供的一個數據庫操作類
SQLiteDatabase是必不可少的,在框架層操作外層物件的方法用 Class<T> 獲取當前的位元組碼 然後建立一個表名 設定快取,在初始化方法裡建立表,那我們如何去獲取到表得名字呢,可以通過java的反射機制,首先建立一個物件,例如User使用者資訊,User類(這裡跳到User程式碼繼續閱讀,然後再返回到當前位置)
回到這裡時候,已經解決了獲取到表名和物件欄位的方法,建立表的具體程式碼邏輯可斷點一步步看下去
public class BaseDao<T> implements IBaseDao<T>{ //操作資料庫,持有資料庫操作的引用 private SQLiteDatabase sqLiteDatabase; //持有操作資料庫所對應的java型別 private Class<T> entityClass; //表名 private String tableName; //標記,用來是否已經存在 private boolean isInit = false; //定義一個快取空間(key - 欄位名 ) private HashMap<String,Field> cacheMap; public boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) { this.sqLiteDatabase = sqLiteDatabase; this.entityClass = entityClass; //自動建表(只需要建一次) if (!isInit){ //如果沒有建過表就建一張新表 tableName=entityClass.getAnnotation(DBTable.class).value(); if (!sqLiteDatabase.isOpen()){ return false; } //執行自動建表的動作 String creatTableSql = getCreateTableSql(); sqLiteDatabase.execSQL(creatTableSql); isInit = true; //初始化快取空間 cacheMap = new HashMap<>(); initCacheMap(); } return isInit; } private void initCacheMap() { //1.取到所有得列表 String sql = "select * from " + tableName +" limit 1,0"; Cursor cursor = sqLiteDatabase.rawQuery(sql,null); String[] columnNames = cursor.getColumnNames(); //2.取所有得成員變數 Field[] clounmnFields = entityClass.getDeclaredFields(); //3.通過2層迴圈讓他們對應起來 for (String columnName: columnNames){ Field resultField = null; for (Field field:clounmnFields){ String fieldAnnotionName = field.getAnnotation(DBField.class).value(); if (columnName.equals(fieldAnnotionName)){ resultField = field; break; } } if (resultField != null){ cacheMap.put(columnName,resultField); } } } /*** * 自動建立表 * @return */ private String getCreateTableSql() { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("create table if not exists "); stringBuffer.append(tableName+"("); //反射得到所有的成員變數 Field[] fields = entityClass.getDeclaredFields(); for (Field field: fields){ Class type = field.getType(); if (type == String.class){ stringBuffer.append(field.getAnnotation(DBField.class).value()+" TEXT,"); }else if (type == Integer.class){ stringBuffer.append(field.getAnnotation(DBField.class).value()+" INTEGER,"); }else if (type == Long.class){ stringBuffer.append(field.getAnnotation(DBField.class).value()+" BIGINT,"); }else if (type == Double.class){ stringBuffer.append(field.getAnnotation(DBField.class).value()+" DOUBLE,"); }else if (type == byte[].class){ stringBuffer.append(field.getAnnotation(DBField.class).value()+" BLOB,"); }else { continue; } } if (stringBuffer.charAt(stringBuffer.length() - 1) == ','){ stringBuffer.deleteCharAt(stringBuffer.length() - 1); } stringBuffer.append(")"); return stringBuffer.toString(); } /** * * @param entity * @return */ @Override public long insert(T entity) { //1.準備好ContentValues Map<String,String> map = getValues(entity); //2.插入內容 ContentValues values = getContentValues(map); //3.執行插入 long result = sqLiteDatabase.insert(tableName,null,values); return result; } /** * 刪除操作 * @param condition * @return */ @Override public int delete(String condition,String[] value) { sqLiteDatabase.delete(tableName,condition,value); return 0; } private ContentValues getContentValues(Map<String, String> map) { ContentValues contentValues = new ContentValues(); Set keys = map.keySet(); Iterator<String> iterator = keys.iterator(); while (iterator.hasNext()){ String key = iterator.next(); String value = map.get(key); if (value != null){ contentValues.put(key,value); } } return contentValues; } private Map<String,String> getValues(T entity) { HashMap<String,String> map = new HashMap<>(); Iterator<Field> fieldItertor = cacheMap.values().iterator(); while (fieldItertor.hasNext()){ Field field = fieldItertor.next(); field.setAccessible(true); //獲取變數的值 try { Object object = field.get(entity); if (object == null){ continue; } String value = object.toString(); //獲取別名 _id name password String key = field.getAnnotation(DBField.class).value(); if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)){ map.put(key,value); } } catch (IllegalAccessException e) { e.printStackTrace(); } } return map; } }
這裡是User物件,大家會發現這裡有個@DBTable,而這個是用來反射表名使用的,如下所示,@Target是註解代表標示的位置,引數有很多,可以對應使用,這裡把他寫在類上的,所以使用ElementType.TYPE,第二個再設定它的保留時間,@Retention,這裡選擇的是RetentionPolicy.RUNTIME,表示執行時,通過DBTable反射機制可以得到表名,而我們需要獲取物件資訊裡所有欄位的話,也需要同樣方式,這裡就繼續建立一個DBField,這些工作做完之後,返回上面繼續BaseDao類程式碼
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBField {
String value();
}
@DBTable("tb_user")
public class User {
@DBField("id")
private Integer id;
@DBField("name")
private String name;
@DBField("password")
private String password;
public User(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
String value();
}
DBField
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBField {
String value();
}
最終的呼叫方式:
BaseDao<User> userDao = BaseDaoFactoty.getOurInstance().getBaseDao(User.class);
userDao.insert(new User(1,".","hahaha"));