1. 程式人生 > >手寫資料庫框架

手寫資料庫框架

目錄

前言

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"));