1. 程式人生 > >android 手把手教你寫資料庫框架(第一篇)

android 手把手教你寫資料庫框架(第一篇)

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

準備寫一個數據庫框架,現在的專案中資料庫框架是用三方orm,不知道是不是叫這個名字,不重要了,準備這段時間把這資料庫框架寫出來,也許寫的不夠好,沒關係,只要堅持住總會比之前好,這就是進步,我們如果不使用資料庫框架的話,寫的步驟可能要多點,現在就開始準備寫,把這個框架命名為android_simple_sqlite

我們之前建立的資料庫檔案都是放在data/data/packageName/xxx.db,放在這裡有一個不好的地方在於如果這個apk被使用者解除安裝了,那麼這個資料庫也會跟著沒了,如果專案需求要求你是在apk被解除安裝了後,apk第二次被安裝時要求做什麼功能需要用到之前資料庫,那麼這個時候你把資料庫放在data/data/packageName/xxx.db就歇菜了!所以我們要針對這部分可以讓資料庫檔案根據使用者的需求存放在哪?

現在畫一個架構設計圖:


看下我的專案包結構:


uml類圖如下:


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

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);
發現在呼叫層只要傳遞對應的實體和操作實體的BaseDao子類即可,因為底層不知道你操作的是什麼具體資料,所以要用到泛型,哪為什麼要getDataHelper()方法中要用到二個泛型呢?

第一個泛型是對資料庫進行增刪改查的具體業務類,你不可能操作User實體物件你傳遞一個PersonDao,這樣會導致去獲取實體物件中的屬性以及屬性上的註解會出現問題

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

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的子類了,既然是框架所以我們想自動的建立表,而不需要手動的去建立,所以我們要使用註解把欄位和表結構對應起來,
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上屬性上,你會發現我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;
}
}
到時候我們只要通過反射去獲取類和屬性上的註解就可以動態的建立表語句

但是前提我們要讓表名和實體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()){//判斷資料庫是否開啟
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);//建立表
}
        cacheMap = new HashMap<>();
initCacheMap();
isInit = true;
}
    return isInit;
}
這是init()方法,如果資料庫沒有開啟,就什麼操作都不做了,如果資料庫打開了就獲取表名和上面說的對應關係,那麼資料庫我們在哪開啟呢?

我們在DataFactory類做成了單例,在建構函式中做資料庫建立和開啟的操作

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

開啟或者建立資料庫的方法如下:

/**
 * 開啟資料庫
*/
private void openDatabase() {
    this.sqLiteDatabase=SQLiteDatabase.openOrCreateDatabase(dbFilePath,null);
}
在上面的BaseDao類上的init()方法中我們是先要建立一個表,為什麼?因為等下我要拿到表中的所有欄位名,不建立表怎麼能拿的到,
try {
    sql =  createTable(clazz.newInstance());
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}
if(!TextUtils.isEmpty(sql)){
    dataBase.execSQL(sql);//建立表
}
上面的createTable()方法是BaseDao中的一個抽象方法,UserDao是繼承了BaseDao,所以要實現這個createTable()方法,這個方法就是自動建立表的過程
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;
}
}
這是通過反射和註解對建立表語句的拼寫!

表建立成功了後就是找對映關係了,封裝在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卡下面建立一個目錄
*/
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 資料庫名
* @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;
}
}
現在插入一條資料試試看!
/**
 * 插入單條資料
*/
private void addSingleData() {
    User user = new User();
user.setAge(18);
user.setName("zhouguizhi");
user.setPassword("123456");
baseDao.insert(user);
}
執行起來發現在我們sd卡目錄下建立的zgz目錄存放了資料庫檔案user.db


看下user表什麼都沒:


我們現在點選下button


現在再把資料庫檔案匯入出來看看:


ok,資料是插入成功了,這是做單條資料插入,有時間會把插入多條資料邏輯寫下,我把我專案放在這部落格中,有需要看的去下載看下,用文字描述太難,不知道怎麼說!

程式碼下載地址:

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

相關推薦

android 手把手資料庫框架(第一)

前言:當你想成為什麼樣的人,努力朝目標努力,終究有一天會實現,人生最大的快樂就是不斷追尋夢想的過程 準備寫一個數據庫框架,現在的專案中資料庫框架是用三方orm,不知道是不是叫這個名字,不重要了,準備這段時間把這資料庫框架寫出來,也許寫的不夠好,沒關係,只要堅持住總會比之前

Android開發之手把手ButterKnife框架(一)

系列文章目錄導讀: 一、概述 JakeWharton我想在Android界無人不知,無人不曉的吧, ButterKnife這個框架就是出自他隻手。這個框架我相信很多人都用過,本系列部落格就是帶大家更加深入的認識這個框架,ButterKnife截至目前

Android開發之手把手ButterKnife框架(三)

系列文章目錄導讀: 一、概述 然後在Processor裡生成自己的程式碼,把要輸出的類,通過StringBuilder拼接字串,然後輸出。 try { // write the file JavaFileObject

手把手DI_1_DI框架有什麼?

DI框架有什麼? 在上一節:手把手教你寫DI_0_DI是什麼? 我們已經理解DI是什麼 接下來我們就徒手擼一擼,玩個支援建構函式注入的DI出來 首先我們回顧一下 建構函式注入 的程式碼形式, 大概長這模樣: class MovieLister { private IMovieFinder f

手把手DI_1_DI框架有什麽?

這樣的 desc inject var .net 框架 抽象 幹什麽 depend DI框架有什麽? 在上一節:手把手教你寫DI_0_DI是什麽? 我們已經理解DI是什麽 接下來我們就徒手擼一擼,玩個支持構造函數註入的DI出來 首先我們回顧一下 構造函數註入 的代碼形式,

小程序初體驗:手把手第一個小程序(一)

輸入框 個人 創建 公測 快速 nsh 成功 too 調用 本文筆者將根據quick start中的範例代碼,帶大家簡單地剖析一下小程序的運行方式,並介紹小程序開發中一些通用的特性,帶著大家一步步寫出自己的小程序。 適用對象:前端初學者,對小程序開發感興趣者 tip

Swing:LookAndFeel 教程第一——手把手出自己的 LookAndFeel

畢業 lean 能開 AC alc 我不 cli private 基礎 本文是 LookAndFeel 系列教程的第一篇。 是我在對 Swing 學習摸索中的一些微

手把手一個基於RxJava的擴充套件框架

今日科技快訊近日訊息,由於央行不再新設第三方支付機構,網際網路企業們只能買買買。最近有通知稱,中

Http框架(二)——三個樣例帶深入理解AsyncTask

func implement oncreate 其它 層疊 worker dcl 例如 人員 這個標題大家不要奇怪,扯Http框架怎麽扯到AsyncTask去了,有兩個原因:首先是Http框架除了核心http理論外。其技術實現核心也是線程池 + 模板 +

大神手把手一個頁面模板引擎,只需20行Javascript代碼!

[1] 表達 最終 strong ice ali 開頭 syntax years 只用20行Javascript代碼就寫出一個頁面模板引擎的大神是AbsurdJS的作者,下面是他分享的全文,轉需。 不知道你有木有聽說過一個基於Javascript的Web頁面預處理器,叫做A

手把手跑 Larave 框架實戰筆記系列之一

PHP Laravel Web開發 PhpStorm Composer [宗旨]嚴格遵循現代編程組件化Web開發原理,通過phpRE+Composer + PhpStorm+Laravel快捷安裝配置集成強大現代優雅開發平臺,實現一站式Web開發。 [前 言]據調查顯示,目前45-54歲之

手把手跑Larave框架實戰筆記系列之二

Laravel PHP框架 系列之一為童鞋們搭好了舞臺,今天正式登臺跑框架了…… 從“路由”開始,玩一把“Hellow world!” Route::get(‘/’, function () { //return view(‘welcome’); return ‘Hellow world!’; });

CSAPP Lab:Attack Lab——手把手病毒(大誤

png -o spa 字符 寄存器 產生 方法 函數返回 發現 這個實驗要求在5個有不同安全漏洞的代碼上進行攻擊,攻擊分為兩部分: Part 1:Code Injection Attacks(緩沖區溢出攻擊) Part 2:Return-Oriented Programmi

手把手DI_2_小白徒手擼建構函式注入

小白徒手擼建構函式注入 在上一節:手把手教你寫DI_1_DI框架有什麼? 我們已經知道我們要擼哪些東西了 那麼我們開始動工吧,這裡呢,我們找小白同學來表演下 小白同學 :我們先定義一下我們的廣告招聘紙有什麼: public abstract class ServiceDefintion // 小白

手把手DI_0_DI是什麼?

DI是什麼? Dependency Injection 常常簡稱為:DI。 它是實現控制反轉(Inversion of Control – IoC)的一個模式。 fowler 大大大神 “幾十年”前的經典文章 https://www.martinfowler.com/articles/i

文案圈內的拿破崙:新媒體推廣運營文案創作的黃金法則,黎想手把手出黃金文案

正式交大家進行黃金文案創作前,先由藝形藝意工作室創始人黎想給大家分享一下移動網際網路的熱潮: 2017年移動網際網路的熱潮是知識付費; 2018年移動網際網路的熱潮是新媒體推廣(自媒體)創作; 2019年移動網際網路的熱潮必定是小程式 2018年在新媒體

手把手搭建Vue框架

Vue專案環境搭建 1、 安裝Node.js雙擊安裝node-v8.9.1-x64.exe。 安裝完成後輸入命令 node -v ,若顯示Node.js版本,既安裝成功。 2、 基於node.js,利用淘寶npm映象安裝相關依賴。控制檯命令: 在cmd裡直接輸入:npm install -

手把手網路爬蟲(2):迷你爬蟲架構

語言&環境 有需要Python學習資料的小夥伴嗎?小編整理【一套Python資料、原始碼和PDF】,感興趣者可以加學習群:548377875或者加小編微信:【mmp9972】反正閒著也是閒著呢,不如學點東西啦~~ 語言:帶足彈藥,繼續用Python開路! t

微信小程式——手把手一個微信小程式

前言   微信小程式年前的跳一跳確實是火了一把,然後呢一直沒有時間去實踐專案,一直想搞但是工作上不需要所以,嗯嗯嗯嗯嗯emmmmm..... 需求   小程式語音識別,全景圖片觀看,登入授權,獲取個人基本資訊 一:基礎框架 跟著官方文件一步一步來,新建一個小程式專案就好 然後呢,畢竟預設的只是基本骨架

手把手一個手勢密碼解鎖View(GesturePasswordView)

相信大家在很多的app肯定看到過手勢密碼解鎖View,但是大家有沒有想過怎麼實現這樣一個View,哈,接下來,小編手把手教大家教寫一個GesturePasswordView。 先看一張效果圖 要實現這樣一個效果,首先需要在螢幕上繪製一個3x3九宮圖,如下圖 具體思路: