国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

android 手把手教你寫數(shù)據(jù)庫框架(第一篇)

2019-11-09 17:37:27
字體:
供稿:網(wǎng)友

前言:當你想成為什么樣的人,努力朝目標努力,終究有一天會實現(xiàn),人生最大的快樂就是不斷追尋夢想的過程

準備寫一個數(shù)據(jù)庫框架,現(xiàn)在的項目中數(shù)據(jù)庫框架是用三方orm,不知道是不是叫這個名字,不重要了,準備這段時間把這數(shù)據(jù)庫框架寫出來,也許寫的不夠好,沒關系,只要堅持住總會比之前好,這就是進步,我們?nèi)绻皇褂脭?shù)據(jù)庫框架的話,寫的步驟可能要多點,現(xiàn)在就開始準備寫,把這個框架命名為android_simple_sqlite

我們之前創(chuàng)建的數(shù)據(jù)庫文件都是放在data/data/packageName/xxx.db,放在這里有一個不好的地方在于如果這個apk被用戶卸載了,那么這個數(shù)據(jù)庫也會跟著沒了,如果項目需求要求你是在apk被卸載了后,apk第二次被安裝時要求做什么功能需要用到之前數(shù)據(jù)庫,那么這個時候你把數(shù)據(jù)庫放在data/data/packageName/xxx.db就歇菜了!所以我們要針對這部分可以讓數(shù)據(jù)庫文件根據(jù)用戶的需求存放在哪?

現(xiàn)在畫一個架構設計圖:

看下我的項目包結(jié)構:

uml類圖如下:

上面的圖可能畫的不夠好,就就將著看下吧,我們在上層只是和DaoFactory這個類打交道,調(diào)用getDataHelper()方法返回BaseDao,看下調(diào)用層基本調(diào)用:

baseDao = DaoFactory.getInstance(this).getDataHelper(UserDao.class,User.class);
User user = new User();user.setAge(18);user.setName("zhouguizhi");user.setPassWord("123456");baseDao.insert(user);發(fā)現(xiàn)在調(diào)用層只要傳遞對應的實體和操作實體的BaseDao子類即可,因為底層不知道你操作的是什么具體數(shù)據(jù),所以要用到泛型,哪為什么要getDataHelper()方法中要用到二個泛型呢?

第一個泛型是對數(shù)據(jù)庫進行增刪改查的具體業(yè)務類,你不可能操作User實體對象你傳遞一個PersonDao,這樣會導致去獲取實體對象中的屬性以及屬性上的注解會出現(xiàn)問題

第二個泛型是對那個實體對象進行操作

public  synchronized  <T extends BaseDao<M>,M> T getDataHelper(Class<T> clazz, Class<M> entityClass){    BaseDao baseDao=null;    try {        baseDao=clazz.newInstance();        baseDao.init(entityClass,sqLiteDatabase);    } catch (InstantiationException e) {        e.PRintStackTrace();    } catch (IllegalaccessException e) {        e.printStackTrace();    }    return (T) baseDao;}這是在DataFactory類中獲取到BaseDao,具體操作肯定是BaseDao的子類了,既然是框架所以我們想自動的創(chuàng)建表,而不需要手動的去創(chuàng)建,所以我們要使用注解把字段和表結(jié)構對應起來,
package com.sqlite.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by admin on 2017/2/6. */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface AnnTableName {    String value();}這是每個bean實體上的注解,用于聲明表名:
package com.sqlite.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface AnnFiled {    String columnName() default "";//字段名    String  type() default "";//字段類型    int len() default 0;//字段長度}上面的注解用于實體bean上屬性上,你會發(fā)現(xiàn)我AnnFiled注解上定義了三個屬性

columnName:表示表名

type:表示字段什么類型的

len:默認的字段多少長度

使用:

package com.sqlite.bean;/** * Created by admin on 2017/2/6. */import com.sqlite.annotation.AnnFiled;import com.sqlite.annotation.AnnTableName;@AnnTableName("user")public class User {    @AnnFiled(columnName = "age",type ="Integer")    public int age;    @AnnFiled(columnName = "name",type ="varchar",len = 20)    public String name;    @AnnFiled(columnName = "pwd",type ="varchar",len = 20)    public String password;    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }}到時候我們只要通過反射去獲取類和屬性上的注解就可以動態(tài)的創(chuàng)建表語句

但是前提我們要讓表名和實體bean屬性對應起來,而且還有可能就bean上的屬性名和表名不一致的情況,所以我們第一步就要把這關系對應上,第二次就不用了,這就是為什么要在BaseDao中init()方法的作用:

BaseDao.java

/** * 初始化 主要是映射關系是否建立 以及 * @param entityClass * @param sqLiteDatabase * @param */public synchronized boolean init(Class<T> entityClass, SQLiteDatabase sqLiteDatabase) {    if(!isInit){        this.dataBase = sqLiteDatabase;        this.clazz = entityClass;        if(dataBase!=null){            if(!dataBase.isOpen()){//判斷數(shù)據(jù)庫是否打開                return false;            }        }        String sql = "";        if(clazz.getAnnotation(AnnTableName.class)!=null){            tbName = clazz.getAnnotation(AnnTableName.class).value();        }else{            tbName = clazz.getSimpleName().toLowerCase();        }        try {            sql =  createTable(clazz.newInstance());        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }        if(!TextUtils.isEmpty(sql)){            dataBase.execSQL(sql);//創(chuàng)建表        }        cacheMap = new HashMap<>();        initCacheMap();        isInit = true;    }    return isInit;}這是init()方法,如果數(shù)據(jù)庫沒有打開,就什么操作都不做了,如果數(shù)據(jù)庫打開了就獲取表名和上面說的對應關系,那么數(shù)據(jù)庫我們在哪打開呢?

我們在DataFactory類做成了單例,在構造函數(shù)中做數(shù)據(jù)庫創(chuàng)建和打開的操作

private DaoFactory(){    dbFilePath= FileUtils.getDataBasePath(ctx,"zgz","user.db");    LogUtils.e("dbFilePath="+dbFilePath);    openDatabase();}上面dbFilePath是你數(shù)據(jù)庫存放的路徑在哪,我們平時數(shù)據(jù)庫都是通過幾次SqliteOpenHelper,那么這時候默認在data/data/package/database/xx.db,在這個框架中默認是在sd卡中如果sd卡不存在就放在其他目錄下.

打開或者創(chuàng)建數(shù)據(jù)庫的方法如下:

/** * 打開數(shù)據(jù)庫 */private void openDatabase() {    this.sqLiteDatabase=SQLiteDatabase.openOrCreateDatabase(dbFilePath,null);}在上面的BaseDao類上的init()方法中我們是先要創(chuàng)建一個表,為什么?因為等下我要拿到表中的所有字段名,不創(chuàng)建表怎么能拿的到,
try {    sql =  createTable(clazz.newInstance());} catch (InstantiationException e) {    e.printStackTrace();} catch (IllegalAccessException e) {    e.printStackTrace();}if(!TextUtils.isEmpty(sql)){    dataBase.execSQL(sql);//創(chuàng)建表}上面的createTable()方法是BaseDao中的一個抽象方法,UserDao是繼承了BaseDao,所以要實現(xiàn)這個createTable()方法,這個方法就是自動創(chuàng)建表的過程
package com.sqlite.dao;import com.sqlite.annotation.AnnFiled;import com.sqlite.annotation.AnnTableName;import com.sqlite.bean.User;import com.sqlite.constant.Constant;import com.sqlite.dao.base.BaseDao;import com.sqlite.utils.LogUtils;import java.lang.reflect.Field;import java.util.HashMap;import java.util.List;import java.util.Map;/** * Created by admin on 2017/2/6. */public class UserDao extends BaseDao<User> {    @Override    protected String createTable(User user) {        String tb_name =  User.class.getAnnotation(AnnTableName.class).value();        Field[] fields =  User.class.getDeclaredFields();        String fieldName = "";        String type = "";        int len = 0;        Map<String,Field> columsMap = new HashMap<>();        for(Field field:fields){            columsMap.put(field.getName(),field);        }        StringBuffer sb = new StringBuffer(Constant.create_table_prix);        sb.append(" ");        sb.append(tb_name);        boolean isFirst = false;        int i=0;        for (Map.Entry<String, Field> entry: columsMap.entrySet()) {            Field filed = entry.getValue();            if(!isFirst){                isFirst = true;                sb.append("(");            }            if(filed.getAnnotation(AnnFiled.class)!=null){                fieldName=filed.getAnnotation(AnnFiled.class).columnName();                type = filed.getAnnotation(AnnFiled.class).type();                len = filed.getAnnotation(AnnFiled.class).len();                sb.append(fieldName).append(" ").append(type).append(len>0?"("+len+")":"").append((i==columsMap.size()-1)? ")":",");                i++;            }        }        LogUtils.e(sb.toString());        return sb.toString();    }    @Override    public Long insert(List<User> entity) {        return null;    }}這是通過反射和注解對創(chuàng)建表語句的拼寫!

表創(chuàng)建成功了后就是找映射關系了,封裝在initCacheMap()方法中,畫一個圖就理解了

用文字太難描述了,可能是文采太差的原因,還是畫圖好

FileUtils.java是對文件操作的工具類在這貼下:

package com.sqlite.utils;import android.content.Context;import android.os.Environment;import android.text.TextUtils;import java.io.File;import java.io.IOException;/** * Created by Adminis on 2017/2/6. */public class FileUtils {    /**     * 判斷sd卡是否存在     * @return     */    public static boolean sdCardIsExites(){        if(Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){                return true;        }        return false;    }    /**     * 獲取sd卡根目錄     * @return     */    public static String getPath(){        String path = "";        if(sdCardIsExites()){            path = Environment.getExternalStorageDirectory().getAbsolutePath();        }        return path;    }    /**     * sd卡下面創(chuàng)建一個目錄     */    public static String createFolder(Context context, String folderName){        File file = null;        String sdRootPath = getPath();        if(TextUtils.isEmpty(sdRootPath)){//表示sd卡不存在            if(context!=null){                // /data/data/com.sqlite/files/zhgougui                file = new File(context.getFilesDir().getAbsolutePath()+File.separator+folderName);            }        }else{            file = new File(sdRootPath+File.separator+folderName);        }        if(file!=null&&!file.exists()){            file.mkdir();        }        return file.getAbsolutePath();    }    /**     *     * @param ctx 上下文     * @param folderName 文件夾名     * @param dataBaseName 數(shù)據(jù)庫名     * @return     */    public static String getDataBasePath(Context ctx,String folderName,String dataBaseName){        String dbPath = "";        String dirPath = createFolder(ctx,folderName);        File file = new File(dirPath+File.separator+dataBaseName);        if(file!=null&&!file.exists()){            try {                file.createNewFile();            } catch (IOException e) {                e.printStackTrace();            }        }        dbPath = file.getAbsolutePath();        return dbPath;    }}現(xiàn)在插入一條數(shù)據(jù)試試看!
/** * 插入單條數(shù)據(jù) */private void addSingleData() {    User user = new User();    user.setAge(18);    user.setName("zhouguizhi");    user.setPassword("123456");    baseDao.insert(user);}運行起來發(fā)現(xiàn)在我們sd卡目錄下創(chuàng)建的zgz目錄存放了數(shù)據(jù)庫文件user.db

看下user表什么都沒:

我們現(xiàn)在點擊下button

現(xiàn)在再把數(shù)據(jù)庫文件導入出來看看:

ok,數(shù)據(jù)是插入成功了,這是做單條數(shù)據(jù)插入,有時間會把插入多條數(shù)據(jù)邏輯寫下,我把我項目放在這博客中,有需要看的去下載看下,用文字描述太難,不知道怎么說!

代碼下載地址:

http://download.csdn.net/detail/coderinchina/9748953


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 连州市| 扶风县| 永川市| 苗栗市| 奉新县| 伊金霍洛旗| 洪泽县| 南阳市| 鹤山市| 葫芦岛市| 曲沃县| 华池县| 溆浦县| 临江市| 山东省| 英山县| 永昌县| 句容市| 林甸县| 江城| 泰来县| 海林市| 兴海县| 威海市| 吴江市| 满洲里市| 贵德县| 温宿县| 永寿县| 固始县| 浪卡子县| 富宁县| 馆陶县| 噶尔县| 武汉市| 中超| 马山县| 新干县| 青川县| 安庆市| 普定县|