前言:當你想成為什么樣的人,努力朝目標努力,終究有一天會實現(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
新聞熱點
疑難解答
圖片精選