Architecture(4)LitePal源碼分析

概述

SQLite

在介紹LitePal之前還是要先介紹一下SQLite淆衷,也就是我們通常所說(shuō)的數(shù)據(jù)庫(kù)旺遮,開(kāi)發(fā)中多多少少會(huì)用到培己,不過(guò)原生的SQLiteDatabase炕横,只要寫(xiě)過(guò)你就知道依溯,寫(xiě)Demo還是可以的老厌,但是在實(shí)際項(xiàng)目中就不夠靈活了,因?yàn)镴ava作為面向?qū)ο蟮恼Z(yǔ)言黎炉,我們?cè)趯?shí)際開(kāi)發(fā)的過(guò)程中操作的大部分都是對(duì)象枝秤,如果使用SQLiteDatabase,我們進(jìn)行CRUD操作的時(shí)候需要寫(xiě)SQL語(yǔ)句慷嗜,查詢(xún)的也是一個(gè)Cursor淀弹。所以需要根據(jù)就跟網(wǎng)絡(luò)請(qǐng)求一樣,如果是簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求庆械,你可以用HttpURLConnection或者OKHttpURLConnection薇溃,但是真正的項(xiàng)目開(kāi)發(fā)的時(shí)候,也是各種框架用地飛起缭乘,所以就有人對(duì)DB的CRUD操作進(jìn)行了封裝沐序,于是就產(chǎn)生了很多的ORM框架,LitePal便是其中的一種堕绩。

LitePal

LitePal的作者是郭霖策幼,使用方式比較簡(jiǎn)潔,從名字來(lái)看LitePal比較輕奴紧,翻譯過(guò)來(lái)是特姐,'輕的朋友',GitHub上面有很多的ORM(Object Relational Mapping)黍氮,也就是通常所說(shuō)的關(guān)系型數(shù)據(jù)庫(kù)框架唐含。常見(jiàn)有greenDAOLitePal沫浆,OrmLite等等捷枯,下面看一下主流的ORM框架在GitHub的使用情況

ORM Initial Commit Star Fork Contributors
greenDAO 2011-07 9366 2537 6
Realm 2012-04 9018 1427 76
LitePal 2013-03 4361 1088 1
Sugar 2011-08 2484 596 57
Ormlite 2010-09 1296 343 6

上面的表格是按照GitHub上的Star數(shù)來(lái)排序的,可以看到LitePal是排第三件缸,F(xiàn)ork數(shù)跟Star數(shù)量基本上是正相關(guān)的铜靶,但是由于LitePal推出的比較晚,而且是作者一人在維護(hù)(Contributors數(shù)據(jù)摘自GitHub),所以郭神還是相當(dāng)厲害的争剿。

LitePal的使用很簡(jiǎn)單已艰,下面以官方的Sample舉例,可以在asset下的literpal.xml中配置DB的版本以及存儲(chǔ)的數(shù)據(jù)對(duì)象蚕苇,存儲(chǔ)路徑哩掺,:

<?xml version="1.0" encoding="utf-8"?>
<litepal>
    <dbname value="sample" />
    <version value="1" />
    <list>
        <mapping class="org.litepal.litepalsample.model.Album" />
        <mapping class="org.litepal.litepalsample.model.Song" />
        <mapping class="org.litepal.litepalsample.model.Singer" />
    </list>
    <storage value="external" />

</litepal>

本文分析的是GitHub上LitePal的最新版本1.6.1,使用方式比較簡(jiǎn)單涩笤,并且作者專(zhuān)門(mén)開(kāi)了一個(gè)專(zhuān)欄:Android數(shù)據(jù)庫(kù)高手秘籍嚼吞,關(guān)于LItePal的使用可以看一下作者的專(zhuān)欄,還有GitHub上的Sample也很詳細(xì)地介紹了這個(gè)框架蹬碧,下面主要從源碼的角度來(lái)解讀一下Litepal的實(shí)現(xiàn)原理舱禽,其實(shí)分析起來(lái)還是很簡(jiǎn)單的,就是注釋有些翻譯起來(lái)很痛苦恩沽,強(qiáng)烈建議來(lái)一個(gè)中文版的注釋誊稚。

正文

工作流程

Litepal是個(gè)ORM框架,所以不像AsncTask罗心,Volley里伯,Picasso那樣流程比較復(fù)雜,以及線(xiàn)程切換等渤闷,它的中心在于讓DB操作更加簡(jiǎn)單跟高效疾瓮,基本上跟數(shù)據(jù)庫(kù)打過(guò)交道都知道,數(shù)據(jù)庫(kù)的主要操作就是CRUD飒箭,然后稍微麻煩點(diǎn)的就是DB的創(chuàng)建狼电,升級(jí)等,說(shuō)白了就是編寫(xiě)SQL語(yǔ)句比較麻煩补憾,畢竟做Android客戶(hù)端開(kāi)發(fā)不像后臺(tái)天天跟數(shù)據(jù)庫(kù)打交道漫萄,隨手一個(gè)SQL語(yǔ)句信手拈來(lái),LitePal將DB的操作封裝了對(duì)象的操作盈匾,也就是我們通常所說(shuō)的ORM操作腾务,這樣操作起來(lái)就會(huì)比較方便了,我們不需要擔(dān)心SQL語(yǔ)句編寫(xiě)錯(cuò)誤削饵,平時(shí)怎么操作對(duì)象岩瘦,現(xiàn)在早怎么操作數(shù)據(jù)庫(kù),同時(shí)Litepal也保留了原始的SQLite語(yǔ)句查詢(xún)方式窿撬。

下面從跟隨Sample中的示例代碼启昧,LitePal的Save操作,跟著源碼來(lái)追一下Litepal的工作流程

Singer是一個(gè)繼承自DataSupport的類(lèi)劈伴,調(diào)用一下save方法密末,即可觸發(fā)DB的存儲(chǔ)造作,跟一下源碼

Singer singer = new Singer();
singer.setName(mSingerNameEdit.getText().toString());
singer.setAge(Integer.parseInt(mSingerAgeEdit.getText().toString()));
singer.setMale(Boolean.parseBoolean(mSingerGenderEdit.getText().toString()));
singer.save();

依然是方法調(diào)用,調(diào)用了SingersaveThrows方法严里,繼續(xù)跟

public synchronized boolean save() {
   try {
      saveThrows();
      return true;
   } catch (Exception e) {
      e.printStackTrace();
      return false;
   }
}

saveThrows是一個(gè)同步方法新啼,在這里通過(guò)Connector獲取到SQLiteDatabase的實(shí)例db,然后開(kāi)啟事務(wù)刹碾,創(chuàng)建了一個(gè)SaveHandler的實(shí)例燥撞,并且在構(gòu)造方法中傳入了SQLiteDatabase,調(diào)用了SingeronSave方法,傳入了當(dāng)前對(duì)象的實(shí)例迷帜,如果執(zhí)行此方法沒(méi)有異常物舒,那么就說(shuō)明存儲(chǔ)成功,關(guān)閉事務(wù)戏锹,否則會(huì)拋出異常冠胯,事務(wù)失敗。那么繼續(xù)看SaveHandler中的onSave方法景用,并且傳入了自身對(duì)象涵叮,所以繼續(xù)跟onSave方法

public synchronized void saveThrows() {
   SQLiteDatabase db = Connector.getDatabase();
   db.beginTransaction();
   try {
      SaveHandler saveHandler = new SaveHandler(db);
      saveHandler.onSave(this);
      clearAssociatedData();
      db.setTransactionSuccessful();
   } catch (Exception e) {
      throw new DataSupportException(e.getMessage(), e);
   } finally {
      db.endTransaction();
   }
}

onSave方法,邏輯稍微復(fù)雜一下伞插,下面通過(guò)一行一行的代碼來(lái)分析一下

void onSave(DataSupport baseObj) throws SecurityException, IllegalArgumentException,
      NoSuchMethodException, IllegalAccessException, InvocationTargetException {
      //拿到對(duì)象的類(lèi)名
   String className = baseObj.getClassName();
   //根據(jù)className,通過(guò)反射獲取到LitePal支持的普通成員變量
   List<Field> supportedFields = getSupportedFields(className);
   //根據(jù)className盾碗,通過(guò)反射獲取到LitePal支持的泛型變量
   List<Field> supportedGenericFields = getSupportedGenericFields(className);
   //根據(jù)className媚污,通過(guò)反射獲得到數(shù)據(jù)庫(kù)的映射關(guān)系
   Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);
   if (!baseObj.isSaved()) {
     //通過(guò)Id判斷是否是首次存儲(chǔ)
           if (!ignoreAssociations) {
             //看表的映射關(guān)系是否需要處理
               analyzeAssociatedModels(baseObj, associationInfos);
           }
            //存儲(chǔ)該列數(shù)據(jù)
           doSaveAction(baseObj, supportedFields, supportedGenericFields);
           if (!ignoreAssociations) {
               analyzeAssociatedModels(baseObj, associationInfos);
           }
   } else {
     //更新操作
           if (!ignoreAssociations) {
               analyzeAssociatedModels(baseObj, associationInfos);
           }
     //更新表中的字段
      doUpdateAction(baseObj, supportedFields, supportedGenericFields);
   }
}

跟一下doSaveAction

private void doSaveAction(DataSupport baseObj, List<Field> supportedFields, List<Field> supportedGenericFields)
      throws SecurityException, IllegalArgumentException, NoSuchMethodException,
      IllegalAccessException, InvocationTargetException {
   values.clear();//清空上一次CRUD操作的ContentValue
   //將Singer中的數(shù)據(jù)轉(zhuǎn)換成ContentValues
   beforeSave(baseObj, supportedFields, values);
   //保存數(shù)據(jù),并且獲取Id
   long id = saving(baseObj, values);
   //對(duì)保存的module賦予Id廷雅,進(jìn)行加密等操作
   afterSave(baseObj, supportedFields, supportedGenericFields, id);
}

跟一下saving這個(gè)方法,發(fā)現(xiàn)已經(jīng)到頭了耗美,直接調(diào)用了SQLiteDataBaseinsert方法

private long saving(DataSupport baseObj, ContentValues values) {
       if (values.size() == 0) {
           values.putNull("id");
       }
   return mDatabase.insert(baseObj.getTableName(), null, values);
}

好像整個(gè)流程到這里基本上完成了一次保存或者更新的操作,還是比較簡(jiǎn)單的航缀。不過(guò)上面主要是在分析流程商架,有很多細(xì)節(jié)沒(méi)有深入,涉及到的幾個(gè)類(lèi)有DataSupport芥玉,SaveHandler蛇摸,Connector,由于只是進(jìn)行了保存操作灿巧,所以還有很多類(lèi)沒(méi)有涉及到赶袄,類(lèi)似typechangeLitePalBase抠藕,AsyncExecutor饿肺,DataHandler等,這些會(huì)接下來(lái)的LitePal架構(gòu)中進(jìn)行具體的分析盾似。

LitePal架構(gòu)

由于LitePal分了很多包敬辣,而且是通過(guò)功能進(jìn)行劃分的,為了便于直觀(guān)的展示,我將包轉(zhuǎn)化成了思維導(dǎo)圖溉跃,這樣可以更加直觀(guān)地了解整個(gè)LitePal的架構(gòu)汰聋。

Litepal

其實(shí)觀(guān)察一下可以發(fā)現(xiàn),crud包跟tablemanager包是整個(gè)框架的核心喊积,因?yàn)槠鋵?shí)這兩個(gè)包有些東西是有關(guān)聯(lián)的烹困,所以沒(méi)法具體的進(jìn)行劃分,所以現(xiàn)在選取了三個(gè)抽象類(lèi)LitePalBase乾吻,AsyncExecutor髓梅,OrmChange因?yàn)榇蟛糠趾诵念?lèi)都是繼承自這三個(gè)抽象類(lèi)的。

LitePal

注釋

LitePal is an Android library that allows developers to use SQLite database extremely easy.You can initialized it by calling {@link #initialize(Context)} method to make LitePal ready to work. Also you can switch the using database by calling {@link #use(LitePalDB)} and {@link #useDefault()} methods.

LitePal是一個(gè)Android庫(kù)绎签,開(kāi)發(fā)者可以用這個(gè)庫(kù)很容易地操作數(shù)據(jù)庫(kù)枯饿。你可以通過(guò)調(diào)用initialize(Contetext)來(lái)初始化LitePal,當(dāng)然你也可以通過(guò)調(diào)用use(LitePalDB)來(lái)使用指定的數(shù)據(jù)庫(kù)或者調(diào)用useDefault來(lái)使用litepal.xml中默認(rèn)的數(shù)據(jù)庫(kù)诡必。

aesKey

設(shè)置AES加密的key

public static void aesKey(String key) {
    CipherUtil.aesKey = key;
}

isDefaultDatabase

//判斷數(shù)據(jù)庫(kù)是否是默認(rèn)的數(shù)據(jù)庫(kù)
private static boolean isDefaultDatabase(String dbName) {
    if (BaseUtility.isLitePalXMLExists()) {
        if (!dbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {
            dbName = dbName + Const.Config.DB_NAME_SUFFIX;
        }
        LitePalConfig config = LitePalParser.parseLitePalConfiguration();
        String defaultDbName = config.getDbName();
        if (!defaultDbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {
            defaultDbName = defaultDbName + Const.Config.DB_NAME_SUFFIX;
        }
        return dbName.equalsIgnoreCase(defaultDbName);
    }
    return false;
}

removeVersionInSharedPreferences

//移除SP中指定的數(shù)據(jù)庫(kù)版本
private static void removeVersionInSharedPreferences(String dbName) {
    if (isDefaultDatabase(dbName)) {
        SharedUtil.removeVersion(null);
    } else {
        SharedUtil.removeVersion(dbName);
    }
}

LitePalApplication

主要用來(lái)給LitePal操作數(shù)據(jù)庫(kù)添加Context

public LitePalApplication() {
   sContext = this;
}
//已經(jīng)被遺棄
@Deprecated
   public static void initialize(Context context) {
       sContext = context;
   }
//獲取Context
public static Context getContext() {
   if (sContext == null) {
      throw new GlobalException(GlobalException.APPLICATION_CONTEXT_IS_NULL);
   }
   return sContext;
}

LitePalDB

注釋

Configuration of LitePal database. It's similar to litepal.xml configuration, but allows to configure database details at runtime. This is very important when comes to support multiple databases functionality.

LitePal DB的配置信息奢方,類(lèi)似于litepal.xml,不過(guò)可以動(dòng)態(tài)地配置DB爸舒,在需要添加多個(gè)數(shù)據(jù)庫(kù)的時(shí)候這個(gè)功能非常重要

通過(guò)litepal.xml配置的數(shù)據(jù)庫(kù)的時(shí)候只能添加一個(gè)蟋字,通過(guò)LitePalDB可以支持多個(gè)數(shù)據(jù)庫(kù)。

成員變量


private int version;//版本號(hào)
private String dbName;//DB名稱(chēng)
private String storage;//存儲(chǔ)路徑
private boolean isExternalStorage = false;//外部存儲(chǔ)是否又讀取權(quán)限
private List<String> classNames;//映射的類(lèi)名集合

fromDefault

//獲取默認(rèn)的DB扭勉,也就是litepal.xml中定義的DB
public static LitePalDB fromDefault(String dbName) {
    LitePalConfig config = LitePalParser.parseLitePalConfiguration();
    LitePalDB litePalDB = new LitePalDB(dbName, config.getVersion());
    litePalDB.setStorage(config.getStorage());
    litePalDB.setClassNames(config.getClassNames());
    return litePalDB;
}

getClassNames

//獲取映射集合
public List<String> getClassNames() {
    if (classNames == null) {
        classNames = new ArrayList<String>();
      //添加系統(tǒng)自動(dòng)生成的數(shù)據(jù)庫(kù)映射類(lèi)
        classNames.add("org.litepal.model.Table_Schema");
    } else if (classNames.isEmpty()) {
        classNames.add("org.litepal.model.Table_Schema");
    }
    return classNames;
}

OrmChange

注釋

This is abstract super class to map the object field types to database column
types. The purpose of this class is to define a abstract method, and let all
subclasses implement it. Each subclass deals with a kind of changing and each
subclass will do their own logic to finish the changing job.

一個(gè)用于將對(duì)象的成員變量屬性映射成數(shù)據(jù)庫(kù)中表的列的屬性的抽象父類(lèi)鹊奖。這個(gè)類(lèi)的目的在于定義一個(gè)抽象方法,并且讓子類(lèi)去實(shí)現(xiàn)他涂炎。每一個(gè)子類(lèi)處理一種變換并且每個(gè)子類(lèi)各司其職忠聚。

繼承關(guān)系

OrmChange

通過(guò)注釋?zhuān)覀冎?strong>OrmChange是類(lèi)型轉(zhuǎn)換的父類(lèi),然后有六個(gè)子類(lèi)唱捣,也就是數(shù)據(jù)庫(kù)支持的存儲(chǔ)類(lèi)型两蟀,BlobOrm是二進(jìn)制,DecimalOrm是浮點(diǎn)數(shù)類(lèi)型震缭,BooleanOrm是布爾類(lèi)型赂毯,TextOrm是文本類(lèi)型,DateOrm是日期類(lèi)型蛀序。

成員變量

由于OrmChange的只有一個(gè)抽象方法欢瞪,所以我選擇了其中的一個(gè)子類(lèi)DecimalOrm來(lái)分析,比較簡(jiǎn)單徐裸。

public class DecimalOrm extends OrmChange {

   /**
    * If the field type passed in is float or double, it will change it into
    * real as column type.
    */
   @Override
   public String object2Relation(String fieldType) {
      if (fieldType != null) {
         if (fieldType.equals("float") || fieldType.equals("java.lang.Float")) {
            return "real";
         }
         if (fieldType.equals("double") || fieldType.equals("java.lang.Double")) {
            return "real";
         }
      }
      return null;
   }

}

Annotion

LitePal在annotion中有兩個(gè)自定義注解遣鼓,一個(gè)是Encrypt,一個(gè)是Column重贺。

Encrypt

運(yùn)行時(shí)注解骑祟,提供加密算法

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Encrypt {
    /**
     * Set the algorithm for encryption.
     */
    String algorithm();

}

Cloumn

列注解回懦,主要是用來(lái)修飾表中的字段

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
    //變量的值屬性是否能為空,默認(rèn)值為true
    boolean nullable() default true;
    //字段是否唯一次企,默認(rèn)值為false
    boolean unique() default false;
    //字段的默認(rèn)值怯晕,默認(rèn)值為“”
    String defaultValue() default "";
    //映射到數(shù)據(jù)庫(kù)時(shí),是否忽略此字段缸棵,默認(rèn)值是false
    boolean ignore() default false;
}

Model

實(shí)際上沒(méi)有這個(gè)類(lèi)舟茶,我為了分析方便,虛擬了這個(gè)Model堵第,因?yàn)檫@幾個(gè)Module都是在創(chuàng)建表的時(shí)候會(huì)用到的

繼承關(guān)系

Model

下面一一對(duì)這些Module進(jìn)行說(shuō)明吧凉,

ColumnModel

注釋
This is a model class for columns. It stores column name, column type, and column constraints information.

這個(gè)類(lèi)主要用于數(shù)據(jù)庫(kù)中的列,存儲(chǔ)了列的名稱(chēng)踏志,類(lèi)型以及關(guān)于列的一下限制信息

成員變量

private String columnName;//列名
private String columnType;//列的類(lèi)型
private boolean isNullable = true;//是否可以為null
private boolean isUnique = false;//是否具有唯一性
private String defaultValue = "";//默認(rèn)值

主要方法
//設(shè)置默認(rèn)值
public void setDefaultValue(String defaultValue) {
    if ("text".equalsIgnoreCase(columnType)) {
        if (!TextUtils.isEmpty(defaultValue)) {
            this.defaultValue = "'" + defaultValue + "'";
        }
    } else {
        this.defaultValue = defaultValue;
    }
}

//是否是ID列
public boolean isIdColumn() {
    return "_id".equalsIgnoreCase(columnName) || "id".equalsIgnoreCase(columnName);
}

GenericModel

注釋
This is a model class for generic table. It stores table name, value column name, value column type and value id column name. This class is used to create generic tables when generic collection fields are declared in the model class.

用于存放泛型的Model阀捅,存放了表名,列名针余,列的類(lèi)型以及id的列名饲鄙。當(dāng)有泛型的集合在model中聲明的時(shí)候,這個(gè)類(lèi)用來(lái)創(chuàng)建泛型的表圆雁。

成員變量

private String tableName;//表名
private String valueColumnName;//列表
private String valueColumnType;//列的類(lèi)型
private String valueIdColumnName;//在主表中的引用
private String getMethodName;//查詢(xún)的關(guān)聯(lián)的表名

所有的方法都是Get,Set方法忍级,不再贅述。

AssociationsModel

注釋
 This is a model class for table associations. It stores table name,
 associated table name, table name which holds foreign key, and association
 type. Relations have three types. One2One, Many2One and Many2Many. If the
 association type is One2One or Many2One, the foreign key will be on the side
 of tableHoldsForeignKey. If the association is Many2Many, a intermediate join
 table will be built and named by the concatenation of the two target table
 names in alphabetical order with underline in the middle.

表關(guān)系的model摸柄,它主要用來(lái)存儲(chǔ)表名颤练,關(guān)聯(lián)的表名,持有外鍵的表名以及關(guān)聯(lián)的類(lèi)型驱负。表有三種關(guān)聯(lián)關(guān)系:一對(duì)一、多對(duì)一和多對(duì)多患雇。如果關(guān)聯(lián)的類(lèi)型是一對(duì)一或者多對(duì)一跃脊,外鍵就在在關(guān)聯(lián)表中。如果關(guān)聯(lián)關(guān)系是多對(duì)多苛吱,一個(gè)中間表將會(huì)被創(chuàng)建酪术,中間表的名稱(chēng)是相互關(guān)聯(lián)的兩個(gè)表的名稱(chēng)中間加上下劃線(xiàn),關(guān)聯(lián)表的排列順序是按照首字母的的順序進(jìn)行排列翠储。

成員變量
private String tableName;//表名
private String associatedTableName;//關(guān)聯(lián)的表名
private String tableHoldsForeignKey;//持有外鍵的表
private int associationType;//關(guān)聯(lián)類(lèi)型

所有的方法都是Get,Set方法绘雁,不再贅述。

TableModel

注釋
This is a model class for tables. It stores a table name and a HashMap for
columns in the table.

表相關(guān)的model援所,存儲(chǔ)了表名以及存放了所有列的HashMap

這個(gè)之前可能用的是HashMap來(lái)存儲(chǔ)的庐舟,不過(guò)新版中是采用List來(lái)進(jìn)行 存儲(chǔ)的。

成員變量

private String tableName;//表名
//存放列的Module的集合
private List<ColumnModel> columnModels = new ArrayList<ColumnModel>();
private String className;//映射的對(duì)象名稱(chēng)
getColumnModelByName

根據(jù)名稱(chēng)返回指定的列

public ColumnModel getColumnModelByName(String columnName) {
    for (ColumnModel columnModel : columnModels) {
        if (columnModel.getColumnName().equalsIgnoreCase(columnName)) {
            return columnModel;
        }
    }
    return null;
}
containsColumn

是否包含某一列

public boolean containsColumn(String columnName) {
    for (int i = 0; i < columnModels.size(); i++) {
        ColumnModel columnModel = columnModels.get(i);
        if (columnName.equalsIgnoreCase(columnModel.getColumnName())) {
            return true;
        }
    }
    return false;
}
removeColumnModelByName

根據(jù)列名刪除某一列

public void removeColumnModelByName(String columnName) {
    if (TextUtils.isEmpty(columnName)) {
        return;
    }
    int indexToRemove = -1;
    for (int i = 0; i < columnModels.size(); i++) {
        ColumnModel columnModel = columnModels.get(i);
        if (columnName.equalsIgnoreCase(columnModel.getColumnName())) {
            indexToRemove = i;
            break;
        }
    }
    if (indexToRemove != -1) {
        columnModels.remove(indexToRemove);
    }
}

LitePalBase

注釋

 Base class of all the LitePal components. If each component need to
 interactive with other components or they have some same logic with duplicate
 codes, LitePalBase may be the solution.

LitePal組件的基類(lèi)住拭。如果一個(gè)組件需要跟其他的組件進(jìn)行交互挪略,或者由于他們有相同的邏輯以至于導(dǎo)致了重復(fù)的代碼历帚,LitePalBase可能是一個(gè)很好的解決方案。

繼承關(guān)系

LitePalBase

通過(guò)類(lèi)圖可以看到杠娱,LitePal的直接子類(lèi)有兩個(gè):

  • Generator:制造者挽牢,那么就是操作表,最終的子類(lèi)一個(gè)是Dropper:刪除表摊求,一個(gè)是Upgrader:升級(jí)表禽拔。
  • DataHandler:數(shù)據(jù)處理者,表中的數(shù)據(jù)處理最常見(jiàn)的就是CRUD操作室叉,確實(shí)他有五個(gè)子類(lèi)睹栖,UpdateHandler用來(lái)更新表中的數(shù)據(jù),SaveHandler用來(lái)保存表中的數(shù)據(jù)太惠,QueryHandler用來(lái)查詢(xún)表中的數(shù)據(jù)磨淌,DeleteHandler則用來(lái)刪除表中數(shù)據(jù),還有一個(gè)是AssociationsAnalyzer凿渊,關(guān)系分析者梁只,用來(lái)處理表之間的映射關(guān)系的,他的子類(lèi)的有三個(gè)埃脏,One2OneAnalyzer搪锣,Many2OneAnalyzerMany2ManyAnalyzer彩掐,郭神就差用中文命名了构舟,很好懂了,不再多說(shuō)堵幽。

LitePalBase

成員變量
public static final String TAG = "LitePalBase";
//Action標(biāo)志狗超,獲取表的映射關(guān)系種類(lèi)
private static final int GET_ASSOCIATIONS_ACTION = 1;
//Action標(biāo)志,獲取表的具體映射關(guān)系
private static final int GET_ASSOCIATION_INFO_ACTION = 2;
//對(duì)象類(lèi)型映射的類(lèi)型數(shù)組
private OrmChange[] typeChangeRules = { new NumericOrm(), new TextOrm(), new BooleanOrm(),
      new DecimalOrm(), new DateOrm(), new BlobOrm()};
//key為對(duì)象的類(lèi)名朴下,value是DB支持的普通成員變量集合
private Map<String, List<Field>> classFieldsMap = new HashMap<>();
//key為對(duì)象的類(lèi)名努咐,value是DB支持泛型成員變量集合
 private Map<String, List<Field>> classGenericFieldsMap = new HashMap<>();
// AssociationsModel的集合
private Collection<AssociationsModel> mAssociationModels;
//association info的Collection
private Collection<AssociationsInfo> mAssociationInfos;
//GenericModel models的Collection
private Collection<GenericModel> mGenericModels;

通過(guò)成員變量,能夠看出LitePalBase作為父類(lèi)主要是通過(guò)反射把Singer中的成員變量轉(zhuǎn)換成對(duì)應(yīng)的AssociationsModel殴胧,GenericModel渗稍,以及ColumnModule的集合或者M(jìn)ap,封裝了一些獲取這些值的方法团滥,供他的子類(lèi)在表結(jié)構(gòu)更改竿屹,單個(gè)表的增刪改查中進(jìn)行使用。

convertFieldToColumnModel

將對(duì)象中DB支持的字段轉(zhuǎn)化成對(duì)應(yīng)的Model

private ColumnModel convertFieldToColumnModel(Field field) {
    //獲取變量類(lèi)型
    String fieldType = field.getType().getName();
    //根據(jù)類(lèi)型匹配在表中對(duì)應(yīng)的類(lèi)型
    String columnType = getColumnType(fieldType);
    boolean nullable = true;
    boolean unique = false;
    String defaultValue = "";
    Column annotation = field.getAnnotation(Column.class);
   //通過(guò)運(yùn)行時(shí)注解給ColumnModel設(shè)值
    if (annotation != null) {
        nullable = annotation.nullable();
        unique = annotation.unique();
        defaultValue = annotation.defaultValue();
    }
  //初始化ColumnModel
    ColumnModel columnModel = new ColumnModel();
    columnModel.setColumnName(DBUtility.convertToValidColumnName(field.getName()));
    columnModel.setColumnType(columnType);
    columnModel.setNullable(nullable);
    columnModel.setUnique(unique);
    columnModel.setDefaultValue(defaultValue);
    return columnModel;
}
關(guān)聯(lián)關(guān)系

看源碼的時(shí)候感覺(jué)這個(gè)地方是最難理解的灸姊。簡(jiǎn)單來(lái)說(shuō)關(guān)聯(lián)關(guān)系其實(shí)有三種拱燃,一對(duì)一,多對(duì)一厨钻,多對(duì)多扼雏。還有就是單向關(guān)聯(lián)跟雙向關(guān)聯(lián)坚嗜,所以實(shí)際上一共有6種關(guān)聯(lián)關(guān)系。在LitePal中诗充,是通過(guò)對(duì)象跟集合來(lái)區(qū)分的苍蔬。下面舉例說(shuō)明

假設(shè)現(xiàn)在有兩個(gè)類(lèi),A跟B,都繼承自DataSupport

  • 一對(duì)一單向關(guān)聯(lián):A中包含B蝴蜓,但是B中不包含A
public class A extends DataSupport {
    private B b;
}
public class B extends DataSupport{

}
  • 一對(duì)一雙向關(guān)聯(lián):A中包含B碟绑,同時(shí)B中也包含A
public class A extends DataSupport {
    private B b;
}
public class B extends DataSupport{
    private A a;
}
  • 多對(duì)一單向關(guān)聯(lián):A中包含List<B>,同時(shí)B中不包含List<A>

public class A extends DataSupport {
    private List<B> list;
}
public class B extends DataSupport {

}
  • 多對(duì)一雙向關(guān)聯(lián):A中包含List<B>茎匠,同時(shí)B中包含List<A>
public class A extends DataSupport {
    private List<B> list;
}
public class B extends DataSupport {
    private A a;
}
  • 多對(duì)多單向關(guān)聯(lián)

    public class A extends DataSupport {
        private List<B> list;
    }
    public class B extends DataSupport {
        private List<A> list;
    }
    

    ?

  • 多對(duì)多單向關(guān)聯(lián)

    public class A extends DataSupport {
        private List<B> list;
    }
    public class B extends DataSupport {
        private List<A> list;
    }
    

    ?

接下來(lái)的兩個(gè)方法格仲,在理解了上面的概念之后才能更好地理解,不然很難理解

oneToAnyConditions
private void oneToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
    //獲取成員變量的類(lèi)型
    Class<?> fieldTypeClass = field.getType();
    // 判斷l(xiāng)itepal.xml映射的類(lèi)中是否包含fieldTypeClass
    if (LitePalAttr.getInstance().getClassNames().contains(fieldTypeClass.getName())) {
        //通過(guò)反射創(chuàng)建映射的關(guān)系表的類(lèi)
        Class<?> reverseDynamicClass = Class.forName(fieldTypeClass.getName());
        //獲取所有的成員變量
        Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
        //是否是雙向關(guān)聯(lián)的標(biāo)志诵冒,默認(rèn)為false
        boolean reverseAssociations = false;
        for (int i = 0; i < reverseFields.length; i++) {
            Field reverseField = reverseFields[i];
            if (!Modifier.isStatic(reverseField.getModifiers())) {
                Class<?> reverseFieldTypeClass = reverseField.getType();
                //B中含有A,關(guān)聯(lián)關(guān)系是一對(duì)一
                if (className.equals(reverseFieldTypeClass.getName())) {
                    if (action == GET_ASSOCIATIONS_ACTION) {
                   //將AssociationModel添加進(jìn)Collection
                    addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
                                fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);
                    } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                   //將AssociationInfo添加進(jìn)Collection
                   addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
                  fieldTypeClass.getName(), field, reverseField, Const.Model.ONE_TO_ONE);
                    }
                  //雙向關(guān)聯(lián)的標(biāo)志置為true
                    reverseAssociations = true;
                }
               //如果在B類(lèi)中含有泛型集合凯肋,說(shuō)明關(guān)聯(lián)關(guān)系是多對(duì)一
                else if (isCollection(reverseFieldTypeClass)) {
                    String genericTypeName = getGenericTypeName(reverseField);
                    if (className.equals(genericTypeName)) {
                       if (action == GET_ASSOCIATIONS_ACTION) {
                          //將AssociationModel添加進(jìn)Collection
                    addIntoAssociationModelCollection(className, fieldTypeClass.getName(),                          className, Const.Model.MANY_TO_ONE);
                        } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                        //將AssociationInfo添加進(jìn)Collection
                    addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),className, field, reverseField, Const.Model.MANY_TO_ONE);
                        }
                         //雙向關(guān)聯(lián)的標(biāo)志置為true
                        reverseAssociations = true;
                    }
                }
            }
        }
       //reverseAssociations為false,說(shuō)明是單向關(guān)聯(lián)
       //單向關(guān)聯(lián)
        if (!reverseAssociations) {
            if (action == GET_ASSOCIATIONS_ACTION) {
                addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
                        fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);
            } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
                        fieldTypeClass.getName(), field, null, Const.Model.ONE_TO_ONE);
            }
        }
    }
}

manyToAnyConditions
private void manyToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
    //如果A中沒(méi)有集合類(lèi)的成員變量汽馋,那么肯定不是多對(duì)多
    if (isCollection(field.getType())) {
        String genericTypeName = getGenericTypeName(field);
        //A類(lèi)中包含泛型集合類(lèi)
        if (LitePalAttr.getInstance().getClassNames().contains(genericTypeName)) {
            //通過(guò)反射創(chuàng)建一個(gè)B類(lèi)
            Class<?> reverseDynamicClass = Class.forName(genericTypeName);
            //拿到B類(lèi)的所有成員變量
            Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
            //雙向關(guān)聯(lián)的標(biāo)志
            boolean reverseAssociations = false;
            for (int i = 0; i < reverseFields.length; i++) {
                Field reverseField = reverseFields[i];
                // Only map private fields
                if (!Modifier.isStatic(reverseField.getModifiers())) {
                    Class<?> reverseFieldTypeClass = reverseField.getType();
                    //如果B中含有A,說(shuō)明是多對(duì)一
                    if (className.equals(reverseFieldTypeClass.getName())) {
                        if (action == GET_ASSOCIATIONS_ACTION) {
                           //將AssociationModel添加進(jìn)Collection
                            addIntoAssociationModelCollection(className, genericTypeName,
                                    genericTypeName, Const.Model.MANY_TO_ONE);
                        } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                          //將AssociationInfo添加進(jìn)Collection
                        addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,field, reverseField, Const.Model.MANY_TO_ONE);
                        }
                      //雙向關(guān)聯(lián)的標(biāo)志置為true
                        reverseAssociations = true;
                    }
                   //如果B的成員變量有集合類(lèi)
                    else if (isCollection(reverseFieldTypeClass)) {
                        String reverseGenericTypeName = getGenericTypeName(reverseField);
                       //如果B中的泛型跟A相同侮东,那么就是多對(duì)多
                        if (className.equals(reverseGenericTypeName)) {
                            if (action == GET_ASSOCIATIONS_ACTION) {
                               //將AssociationModel添加進(jìn)Collection
                   addIntoAssociationModelCollection(className, genericTypeName,  null,Const.Model.MANY_TO_MANY);
                            } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                              //將AssociationInfo添加進(jìn)Collection
                       addIntoAssociationInfoCollection(className, genericTypeName, null, field,reverseField, Const.Model.MANY_TO_MANY);
                            }
                          //雙向關(guān)聯(lián)的標(biāo)志置為true
                            reverseAssociations = true;
                        }
                    }

                }
            }
           //非雙向關(guān)聯(lián),作為多對(duì)一處理
            if (!reverseAssociations) {
                if (action == GET_ASSOCIATIONS_ACTION) {
                  //將AssociationModel添加進(jìn)Collection
                    addIntoAssociationModelCollection(className, genericTypeName,
                            genericTypeName, Const.Model.MANY_TO_ONE);
                } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                   //將AssociationInfo添加進(jìn)Collection
                    addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName, field, null, Const.Model.MANY_TO_ONE);
                }
            }
        } else if (BaseUtility.isGenericTypeSupported(genericTypeName) && action == GET_ASSOCIATIONS_ACTION) {
           //如果是雙向關(guān)聯(lián)豹芯,并且是獲取關(guān)聯(lián)關(guān)系類(lèi)型的Action下
            Column annotation = field.getAnnotation(Column.class);
           //變量是有效的
            if (annotation != null && annotation.ignore()) {
                return;
            }
          //創(chuàng)建GenericModel
            GenericModel genericModel = new GenericModel();
            genericModel.setTableName(DBUtility.getGenericTableName(className, field.getName()));
     genericModel.setValueColumnName(DBUtility.convertToValidColumnName(field.getName()));
            genericModel.setValueColumnType(getColumnType(genericTypeName));
            genericModel.setValueIdColumnName(DBUtility.getGenericValueIdColumnName(className));
            mGenericModels.add(genericModel);
        }
    }
}

Generator

繼承關(guān)系
Generator
成員變量
private Collection<TableModel> mTableModels;//表的Module集合
private Collection<AssociationsModel> mAllRelationModels;//關(guān)系Module集合
execute

通過(guò)SQL語(yǔ)句建表

protected void execute(List<String> sqls, SQLiteDatabase db) {
   String throwSQL = "";
   try {
      if (sqls != null && !sqls.isEmpty()) {
         for (String sql : sqls) {
                   if (!TextUtils.isEmpty(sql)) {
                     //轉(zhuǎn)換成小寫(xiě)
                       throwSQL = BaseUtility.changeCase(sql);
                       db.execSQL(throwSQL);
                   }
         }
      }
   } catch (SQLException e) {
      throw new DatabaseGenerateException(DatabaseGenerateException.SQL_ERROR + throwSQL);
   }
}
create

調(diào)用Creator的方法創(chuàng)建或更新表

private static void create(SQLiteDatabase db, boolean force) {
   Creator creator = new Creator();
   creator.createOrUpgradeTable(db, force);
}
drop

調(diào)用Dropper的方法刪除表

private static void drop(SQLiteDatabase db) {
   Dropper dropper = new Dropper();
   dropper.createOrUpgradeTable(db, false);
}
upgrade

調(diào)用Upgrader的方法

private static void upgradeTables(SQLiteDatabase db) {
   Upgrader upgrader = new Upgrader();
   upgrader.createOrUpgradeTable(db, false);
}

DataHandler

繼承關(guān)系
DataHandler
成員變量
//DB的實(shí)例用來(lái)進(jìn)行CRUD操作
SQLiteDatabase mDatabase;
//空的DataSupport實(shí)例
private DataSupport tempEmptyModel;
//ModuleA中的AssociationsInfo集合
private List<AssociationsInfo> fkInCurrentModel;
//ModuleB中的AssociationsInfo集合
private List<AssociationsInfo> fkInOtherModel;
query
protected <T> List<T> query(Class<T> modelClass, String[] columns, String selection,
      String[] selectionArgs, String groupBy, String having, String orderBy, String limit,
      List<AssociationsInfo> foreignKeyAssociations) {
   List<T> dataList = new ArrayList<T>();
   Cursor cursor = null;
   try {
     List<Field> supportedFields = getSupportedFields(modelClass.getName());
     List<Field> supportedGenericFields =getSupportedGenericFields(modelClass.getName());
       String[] customizedColumns = DBUtility.convertSelectClauseToValidNames(getCustomizedColumns(columns, supportedGenericFields, foreignKeyAssociations));
     //拿到table的名字
      String tableName = getTableName(modelClass);
     //通過(guò)參數(shù)進(jìn)行查找
      cursor = mDatabase.query(tableName, customizedColumns, selection, selectionArgs,
            groupBy, having, orderBy, limit);
      if (cursor.moveToFirst()) {
     SparseArray<QueryInfoCache> queryInfoCacheSparseArray = new SparseArray<QueryInfoCache>();
               Map<Field, GenericModel> genericModelMap = new HashMap<Field, GenericModel>();
         do {
           //創(chuàng)建泛型實(shí)例
            T modelInstance = (T) createInstanceFromClass(modelClass);
           //進(jìn)行Id賦值
            giveBaseObjIdValue((DataSupport) modelInstance,
                  cursor.getLong(cursor.getColumnIndexOrThrow("id")));
           //將查詢(xún)出來(lái)的Value給Module賦值
            setValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);
           //將泛型的Value賦值給Module
            setGenericValueToModel((DataSupport) modelInstance, supportedGenericFields, genericModelMap);
           //設(shè)置關(guān)聯(lián)對(duì)象的值
            if (foreignKeyAssociations != null) {
               setAssociatedModel((DataSupport) modelInstance);
            }
           //添加元素進(jìn)集合
            dataList.add(modelInstance);
         } while (cursor.moveToNext());
               queryInfoCacheSparseArray.clear();
               genericModelMap.clear();
      }
      return dataList;
   } catch (Exception e) {
      throw new DataSupportException(e.getMessage(), e);
   } finally {
      if (cursor != null) {
         cursor.close();
      }
   }
}
analyzeAssociations

對(duì)關(guān)聯(lián)關(guān)系進(jìn)行分析

private void analyzeAssociations(String className) {
   Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);
   if (fkInCurrentModel == null) {
      fkInCurrentModel = new ArrayList<AssociationsInfo>();
   } else {
      fkInCurrentModel.clear();
   }
   if (fkInOtherModel == null) {
      fkInOtherModel = new ArrayList<AssociationsInfo>();
   } else {
      fkInOtherModel.clear();
   }
   for (AssociationsInfo associationInfo : associationInfos) {
     //關(guān)聯(lián)關(guān)系為多對(duì)一或者一對(duì)一
      if (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE
            || associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {
         if (associationInfo.getClassHoldsForeignKey().equals(className)) {
           //當(dāng)前module
            fkInCurrentModel.add(associationInfo);
         } else {
           //關(guān)聯(lián)Module
            fkInOtherModel.add(associationInfo);
         }
      } else if (associationInfo.getAssociationType() == Const.Model.MANY_TO_MANY) {
        //多對(duì)多
         fkInOtherModel.add(associationInfo);
      }
   }
}
analyzeAssociatedModels
protected void analyzeAssociatedModels(DataSupport baseObj, Collection<AssociationsInfo> associationInfos) {
   try {
      for (AssociationsInfo associationInfo : associationInfos) {
        //遍歷associationInfos集合
         if (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE) {
           //Many2OneAnalyzer處理
            new Many2OneAnalyzer().analyze(baseObj, associationInfo);
         } else if (associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {
           //One2OneAnalyzer處理
            new One2OneAnalyzer().analyze(baseObj, associationInfo);
         } else if (associationInfo.getAssociationType() == Const.Model.MANY_TO_MANY) {
           //Many2ManyAnalyzer處理
            new Many2ManyAnalyzer().analyze(baseObj, associationInfo);
         }
      }
   } catch (Exception e) {
      throw new DataSupportException(e.getMessage(), e);
   }
}

AssociationsAnalyzer的子類(lèi)實(shí)際上是在處理AssociationsInfo悄雅,就是將AssociationsInfo分配給當(dāng)前的Module或者是關(guān)聯(lián)的Module

encryptValue

LitePal支持MD5加密跟AES加密,但是MD5加密之后是不能解密的铁蹈,不知道MD5有什么用途

protected Object encryptValue(String algorithm, Object fieldValue) {
    if (algorithm != null && fieldValue != null) {
        if (DataSupport.AES.equalsIgnoreCase(algorithm)) {
            fieldValue = CipherUtil.aesEncrypt((String) fieldValue);
        } else if (DataSupport.MD5.equalsIgnoreCase(algorithm)) {
            fieldValue = CipherUtil.md5Encrypt((String) fieldValue);
        }
    }
    return fieldValue;
}
decryptValue
 */
protected Object decryptValue(String algorithm, Object fieldValue) {
    if (algorithm != null && fieldValue != null) {
        if (DataSupport.AES.equalsIgnoreCase(algorithm)) {
            fieldValue = CipherUtil.aesDecrypt((String) fieldValue);
        }
    }
    return fieldValue;
}

SaveHandler

方法概覽
SavedHandler
onSave
void onSave(DataSupport baseObj) throws SecurityException, IllegalArgumentException,
      NoSuchMethodException, IllegalAccessException, InvocationTargetException {
   //獲取類(lèi)名
   String className = baseObj.getClassName();
   //獲取DB支持的成員變量
   List<Field> supportedFields = getSupportedFields(className);
   //獲取DB支持的泛型變量
   List<Field> supportedGenericFields = getSupportedGenericFields(className);
   //獲取所有的關(guān)聯(lián)關(guān)系
   Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);
   //是否存儲(chǔ)過(guò)
   if (!baseObj.isSaved()) {
           if (!ignoreAssociations) {
               analyzeAssociatedModels(baseObj, associationInfos);
           }
            //進(jìn)行數(shù)據(jù)存儲(chǔ)
            doSaveAction(baseObj, supportedFields, supportedGenericFields);
           if (!ignoreAssociations) {
               analyzeAssociatedModels(baseObj, associationInfos);
           }
   } else {
           if (!ignoreAssociations) {
               analyzeAssociatedModels(baseObj, associationInfos);
           }
       //更新操作
        doUpdateAction(baseObj, supportedFields, supportedGenericFields);
   }
}
doSaveAction
private void doSaveAction(DataSupport baseObj, List<Field> supportedFields, List<Field> supportedGenericFields)
      throws SecurityException, IllegalArgumentException, NoSuchMethodException,
      IllegalAccessException, InvocationTargetException {
   //清空ContentValues
   values.clear();
   //將成員變量轉(zhuǎn)化成Values
   beforeSave(baseObj, supportedFields, values);
   //進(jìn)行插入操作宽闲,拿到返回的ID
   long id = saving(baseObj, values);
   //如果有關(guān)聯(lián)表,則進(jìn)行關(guān)聯(lián)表的插入
   afterSave(baseObj, supportedFields, supportedGenericFields, id);
}
doUpdateAction
private void doUpdateAction(DataSupport baseObj, List<Field> supportedFields, List<Field> supportedGenericFields)
      throws SecurityException, IllegalArgumentException, NoSuchMethodException,
      IllegalAccessException, InvocationTargetException {
  //清空ContentValues
   values.clear();
   //將成員變量轉(zhuǎn)化成Values
   beforeUpdate(baseObj, supportedFields, values);
   //進(jìn)行更新操作
   updating(baseObj, values);
   //進(jìn)行關(guān)聯(lián)表操作
   afterUpdate(baseObj, supportedGenericFields);
}

QueryHandler

方法概覽
QueryHandler
onFindFirst

找到第一個(gè)

<T> T onFindFirst(Class<T> modelClass, boolean isEager) {
   List<T> dataList = query(modelClass, null, null, null, null, null, "id", "1",
         getForeignKeyAssociations(modelClass.getName(), isEager));
   if (dataList.size() > 0) {
      return dataList.get(0);
   }
   return null;
}
onFindAll
<T> List<T> onFindAll(Class<T> modelClass, boolean isEager, long... ids) {
   List<T> dataList;
   if (isAffectAllLines(ids)) {
      dataList = query(modelClass, null, null, null, null, null, "id", null,
            getForeignKeyAssociations(modelClass.getName(), isEager));
   } else {
      dataList = query(modelClass, null, getWhereOfIdsWithOr(ids), null, null, null, "id",
            null, getForeignKeyAssociations(modelClass.getName(), isEager));
   }
   return dataList;
}

Query

方法概覽
QueryHandler
update
int onUpdate(DataSupport baseObj, long id) throws SecurityException, IllegalArgumentException,
      NoSuchMethodException, IllegalAccessException, InvocationTargetException {
   List<Field> supportedFields = getSupportedFields(baseObj.getClassName());
   List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());
       updateGenericTables(baseObj, supportedGenericFields, id);
   ContentValues values = new ContentValues();
   //轉(zhuǎn)換成員賓亮
   putFieldsValue(baseObj, supportedFields, values);
   //設(shè)置默認(rèn)值
   putFieldsToDefaultValue(baseObj, values, id);
   if (values.size() > 0) {
      return mDatabase.update(baseObj.getTableName(), values, "id = " + id, null);
   }
   return 0;
}
qanalyzeAssociations
    private void analyzeAssociations(DataSupport baseObj) {
        try {
            Collection<AssociationsInfo> associationInfos = getAssociationInfo(baseObj
                    .getClassName());
          //關(guān)聯(lián)表查詢(xún)
            analyzeAssociatedModels(baseObj, associationInfos);
        } catch (Exception e) {
            throw new DataSupportException(e.getMessage(), e);
        }
    }

DeleteHandler

方法概覽
DeleteHandler
onDelete
int onDelete(Class<?> modelClass, long id) {
  //獲取泛型變量集合
   List<Field> supportedGenericFields = getSupportedGenericFields(modelClass.getName());
   //刪除關(guān)聯(lián)表
    deleteGenericData(modelClass, supportedGenericFields, id);
   //分析關(guān)聯(lián)表
   analyzeAssociations(modelClass);
   int rowsAffected = deleteCascade(modelClass, id);
   rowsAffected += mDatabase.delete(getTableName(modelClass),"id = " + id, null);
  //刪除關(guān)聯(lián)表中的數(shù)據(jù)
   getForeignKeyTableToDelete().clear();
   return rowsAffected;
}

AsyncExecutor

如果只是對(duì)少量的數(shù)據(jù)進(jìn)行操作握牧,實(shí)際上在主線(xiàn)程中操作是沒(méi)有問(wèn)題的容诬,但是如果是大量的數(shù)據(jù),那么就會(huì)比較耗時(shí)沿腰,所以就必須在子線(xiàn)程中進(jìn)行放案,所以L(fǎng)itePal支持在子線(xiàn)程中進(jìn)行DB操作。

繼承關(guān)系

AsyncExecutor
AsyncExecutor
public abstract class AsyncExecutor {

    //后臺(tái)Runnable
    private Runnable pendingTask;
   //提交一個(gè)任務(wù)
    public void submit(Runnable task) {
        pendingTask = task;
    }
   //開(kāi)啟線(xiàn)程
    void execute() {
        if (pendingTask != null) {
            new Thread(pendingTask).start();
        }
    }

}
AverageExecutor
//回調(diào)的接口
public interface AverageCallback {
    void onFinish(double average);
}

public class AverageExecutor extends AsyncExecutor {
    private AverageCallback cb;
   //開(kāi)啟線(xiàn)程
    public void listen(AverageCallback callback) {
        cb = callback;
        execute();
    }
    public AverageCallback getListener() {
        return  cb;
    }

}


使用方式

public static AverageExecutor averageAsync(final String tableName, final String column) {
    final AverageExecutor executor = new AverageExecutor();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (DataSupport.class) {
                final double average = average(tableName, column);
                if (executor.getListener() != null) {
                    LitePal.getHandler().post(new Runnable() {
                        @Override
                        public void run() {
                           //主線(xiàn)程中回調(diào)
                            executor.getListener().onFinish(average);
                        }
                    });
                }
            }
        }
    };
    executor.submit(runnable);
    return executor;
}

另外幾個(gè)Executor實(shí)際上也是這么使用的矫俺,也就是在子線(xiàn)程中進(jìn)行查詢(xún)完成,然后通過(guò)Handler將結(jié)果以post Runnable的形式傳遞到主線(xiàn)程掸冤,通過(guò)接口回調(diào)傳遞操作的結(jié)果厘托。

DataSupport

注釋

DataSupport connects classes to SQLite database tables to establish an almost zero-configuration persistence layer for applications. In the context of an application, these classes are commonly referred to as models. Models can also be connected to other models.
DataSupport relies heavily on naming in that it uses class and association names to establish mappings between respective database tables and foreign key columns.
Automated mapping between classes and tables, attributes and columns.

DataSupport將類(lèi)連接到SQLite數(shù)據(jù)庫(kù)的表以建立一個(gè)應(yīng)用程序幾乎零配置的持久層。在應(yīng)用的Context中稿湿,這些類(lèi)被定義為Module铅匹,Module也可以跟別的Modules產(chǎn)生聯(lián)系。

DataSupport嚴(yán)重依賴(lài)于它使用類(lèi)和關(guān)聯(lián)的命名饺藤。在數(shù)據(jù)庫(kù)表和外部表之間建立映射的名稱(chēng)包斑,外鍵流礁。

在類(lèi)和表、屬性和列之間的自動(dòng)映射罗丰。

delete&deleteAsync

public static synchronized int delete(Class<?> modelClass, long id) {
   int rowsAffected = 0;
   SQLiteDatabase db = Connector.getDatabase();
   db.beginTransaction();
   try {
     //調(diào)用了DeleteHandler
      DeleteHandler deleteHandler = new DeleteHandler(db);
      rowsAffected = deleteHandler.onDelete(modelClass, id);
      db.setTransactionSuccessful();
      return rowsAffected;
   } finally {
      db.endTransaction();
   }
}

saveAll&saveAllAsync

public static synchronized <T extends DataSupport> void saveAll(Collection<T> collection) {
   SQLiteDatabase db = Connector.getDatabase();
   db.beginTransaction();
   try {
     //調(diào)用了SaveHandler
      SaveHandler saveHandler = new SaveHandler(db);
      saveHandler.onSaveAll(collection);
      db.setTransactionSuccessful();
   } catch (Exception e) {
      throw new DataSupportException(e.getMessage(), e);
   } finally {
      db.endTransaction();
   }
}

update&updateAsync

 */
public synchronized int update(long id) {
   try {
     //調(diào)用了UpdateHandler
      UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());
      int rowsAffected = updateHandler.onUpdate(this, id);
      getFieldsToSetToDefault().clear();
      return rowsAffected;
   } catch (Exception e) {
      throw new DataSupportException(e.getMessage(), e);
   }
}

average&averageAsync

public static synchronized double average(String tableName, String column) {
   ClusterQuery cQuery = new ClusterQuery();
   return cQuery.average(tableName, column);
}

接著調(diào)用了ClusterQuery的average方法神帅,ClusterQuery然后調(diào)用了QueryHandler的方法

public synchronized double average(String tableName, String column) {
   QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
   return queryHandler.onAverage(tableName, column, mConditions);
}

max&maxAsync

public static synchronized <T> T max(String tableName, String columnName, Class<T> columnType) {
   ClusterQuery cQuery = new ClusterQuery();
   return cQuery.max(tableName, columnName, columnType);
}

接著調(diào)用了ClusterQuery的max方法,ClusterQuery然后調(diào)用了max的方法

public synchronized <T> T max(String tableName, String columnName, Class<T> columnType) {
   QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
   return queryHandler.onMax(tableName, columnName, mConditions, columnType);
}

CRUD操作

DataSupport是所有映射的對(duì)象的父類(lèi)萌抵,定義了所有的關(guān)于DB的操作方法找御,并且每個(gè)操作都有同步跟異步方法,異步方法最終還是會(huì)調(diào)用同步方法绍填,只是在調(diào)用的時(shí)候加了一個(gè)同步鎖霎桅,有點(diǎn)類(lèi)似Picasso的單例,可以是單個(gè)對(duì)象也就是某一列的操作讨永,也可以是對(duì)整個(gè)DB的操作滔驶,如果是基本的CRUD操作,那么DataSupport會(huì)直接調(diào)用DataHandler的相應(yīng)的子類(lèi)的相應(yīng)的方法去執(zhí)行卿闹。

聚合查詢(xún)

如果是聚合查詢(xún)揭糕,也就是說(shuō)需要多個(gè)條件的查詢(xún),LitePal提供了一個(gè)類(lèi)ClusterQuery比原,可以設(shè)置多個(gè)查詢(xún)條件插佛,并且采用了Builder設(shè)計(jì)模式可以動(dòng)態(tài)的設(shè)置查詢(xún)條件,類(lèi)似average量窘,count雇寇,limit等操作,下面會(huì)重點(diǎn)分析一下這個(gè)類(lèi)蚌铜。

ClusterQuery

注釋

Allows developers to query tables with cluster style.

讓開(kāi)發(fā)者能夠以聚集的風(fēng)格進(jìn)行查詢(xún)锨侯。

這段確實(shí)不知道怎么翻譯,不過(guò)意思就是能夠進(jìn)行聚合查詢(xún)冬殃。

成員變量

String[] mColumns;//查詢(xún)的列
String[] mConditions;//查詢(xún)條件
String mOrderBy;//排序條件
String mLimit;//返回?cái)?shù)據(jù)的數(shù)量
String mOffset;//偏移量囚痴,也就是第幾行開(kāi)始查詢(xún)

構(gòu)造方法

ClusterQuery() {
}

空方法,可以讓外部實(shí)例化

Builder


//設(shè)置要查詢(xún)的列
public ClusterQuery select(String... columns) {
    mColumns = columns;
    return this;
}
//設(shè)置查詢(xún)條件
public ClusterQuery where(String... conditions) {
    mConditions = conditions;
    return this;
}

//設(shè)置排序準(zhǔn)則
public ClusterQuery order(String column) {
    mOrderBy = column;
    return this;
}

//設(shè)置返回?cái)?shù)據(jù)的數(shù)量
public ClusterQuery limit(int value) {
    mLimit = String.valueOf(value);
    return this;
}

//設(shè)置偏移量
public ClusterQuery offset(int value) {
    mOffset = String.valueOf(value);
    return this;
}

ClusterQuery采用了Builder模式审葬,可以動(dòng)態(tài)地進(jìn)行添加參數(shù)深滚,也就是實(shí)現(xiàn)連綴查詢(xún),查詢(xún)起來(lái)比較方便涣觉。

count&countAsync

public CountExecutor countAsync(final String tableName) {
    final CountExecutor executor = new CountExecutor();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (DataSupport.class) {
              //同步獲取查詢(xún)結(jié)果
                final int count = count(tableName);
                if (executor.getListener() != null) {
                    LitePal.getHandler().post(new Runnable() {
                        @Override
                        public void run() {
                            executor.getListener().onFinish(count);
                        }
                    });
                }
            }
        }
    };
    executor.submit(runnable);
    return executor;
}

追一下count方法痴荐,調(diào)用了QueryHandlercount方法

public synchronized int count(String tableName) {
    QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
   //然后添加了構(gòu)造時(shí)候的參數(shù)
    return queryHandler.onCount(tableName, mConditions);
}

min&minAsync

public <T> FindExecutor minAsync(final String tableName, final String columnName, final Class<T> columnType) {
    final FindExecutor executor = new FindExecutor();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (DataSupport.class) {
              //同步獲取最小值的查詢(xún)結(jié)果
                final T t = min(tableName, columnName, columnType);
                if (executor.getListener() != null) {
                    LitePal.getHandler().post(new Runnable() {
                        @Override
                        public void run() {
                          //回調(diào)通知主線(xiàn)程
                            executor.getListener().onFinish(t);
                        }
                    });
                }
            }
        }
    };
    executor.submit(runnable);
    return executor;
}

追一下min方法

public synchronized <T> T min(String tableName, String columnName, Class<T> columnType) {
    QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
     //然后添加了構(gòu)造時(shí)候的查詢(xún)參數(shù)
    return queryHandler.onMin(tableName, columnName, mConditions, columnType);
}

Utils

CipherUtil

這個(gè)主要用來(lái)進(jìn)行加解密的,LitePal采用的是AES加密算法官册,而且加密的key可以在LitePal中進(jìn)行動(dòng)態(tài)設(shè)置生兆,不過(guò)也支持MD5加密,不過(guò)MD5無(wú)法解密

//AES加密
public static String aesEncrypt(String plainText) {
    if (TextUtils.isEmpty(plainText)) {
        return plainText;
    }
    try {
        return AESCrypt.encrypt(aesKey, plainText);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
//AES解密
public static String aesDecrypt(String encryptedText) {
    if (TextUtils.isEmpty(encryptedText)) {
        return encryptedText;
    }
    try {
        return AESCrypt.decrypt(aesKey, encryptedText);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
//MD5加密
public static String md5Encrypt(String plainText) {
    try {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        digest.update(plainText.getBytes(Charset.defaultCharset()));
        return new String(toHex(digest.digest()));
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

BaseUtility

基本的工具類(lèi)膝宁,封裝了一些最基本的方法

  • changeCase:對(duì)傳入的String進(jìn)行大小寫(xiě)轉(zhuǎn)換鸦难,或者保持不變
  • checkConditionsCorrect:檢查查詢(xún)條件是否合理
  • isFieldTypeSupported:檢查成員變量是否合理

DBUtility

  • getTableNameByClassName:通過(guò)類(lèi)名獲取表名
  • isTableExists:查詢(xún)表是否存在

SharedUtil

  • updateVersion:根據(jù)Key升級(jí)當(dāng)前版本號(hào)
  • getLastVersion:根據(jù)Key獲取上一個(gè)版本
  • removeVersion:根據(jù)Key移除特定的版本

總結(jié)

LitePal利用反射對(duì)獲取Model的屬性根吁,然后拼接成SQL語(yǔ)句,對(duì)于調(diào)用者來(lái)說(shuō)減少了很多重復(fù)性操作合蔽,升級(jí)也比較方便击敌,符合Java的面向?qū)ο螅褂谜卟挥迷訇P(guān)心底層的SQL語(yǔ)句辈末,當(dāng)然LitePal同時(shí)也提供了使用SQL語(yǔ)句查詢(xún)的功能愚争,使用者可以憑喜好使用,看了SQL的源碼實(shí)現(xiàn)之后挤聘,個(gè)人覺(jué)得有些地方可以改進(jìn)轰枝。

反射優(yōu)化

LitePal的CRUD操作每次都需要反射,這些在數(shù)據(jù)量少的時(shí)候影響還好组去,在數(shù)據(jù)量較大的時(shí)候會(huì)比較影響效率鞍陨,可以對(duì)已經(jīng)反射過(guò)的Model屬性緩存起來(lái),那么下一次CRUD操作的時(shí)候如果是同一個(gè)Model从隆,那么直接從緩存中取诚撵,那么效率就會(huì)快一些。

同步優(yōu)化

LitePal的CRUD操作全部都使用了synchronized關(guān)鍵字键闺,其實(shí)如果我們的操作是單線(xiàn)程中進(jìn)行寿烟,沒(méi)必要上鎖,可以再提供一個(gè)重載的非同步方法,因?yàn)楹芏鄷r(shí)候CRUD操作都是在單線(xiàn)程中的。

單例優(yōu)化

LitePal的每一次CRUD操作僚楞,都需要對(duì)DataHandler的子類(lèi)SaveHandler支竹、QueryHandler進(jìn)行一次實(shí)例化认烁,如果采用單例結(jié)合Builder模式的話(huà)可以減少一部分開(kāi)銷(xiāo),還有ClusterQuery也是如此。

線(xiàn)程優(yōu)化

LitePal開(kāi)啟多線(xiàn)程的時(shí)候是采用的new Thread方式,如果有多個(gè)異步的DB操作的時(shí)候待锈,會(huì)創(chuàng)建很多個(gè)線(xiàn)程,如果使用線(xiàn)程池可以減少一部分開(kāi)銷(xiāo)嘴高。

其實(shí)上面的一些建議只是我個(gè)人的理解竿音,LitePal已經(jīng)是一款很優(yōu)秀的框架,希望LitePal這款ORM框架越來(lái)越強(qiáng)拴驮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谍失,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子莹汤,更是在濱河造成了極大的恐慌,老刑警劉巖颠印,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纲岭,死亡現(xiàn)場(chǎng)離奇詭異抹竹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)止潮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)窃判,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人喇闸,你說(shuō)我怎么就攤上這事袄琳。” “怎么了燃乍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵唆樊,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我刻蟹,道長(zhǎng)逗旁,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任舆瘪,我火速辦了婚禮片效,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘英古。我一直安慰自己淀衣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布召调。 她就那樣靜靜地躺著膨桥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪某残。 梳的紋絲不亂的頭發(fā)上国撵,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音玻墅,去河邊找鬼介牙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛澳厢,可吹牛的內(nèi)容都是我干的环础。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼剩拢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼线得!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起徐伐,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贯钩,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體角雷,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡祸穷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勺三。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雷滚。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吗坚,靈堂內(nèi)的尸體忽然破棺而出祈远,到底是詐尸還是另有隱情,我是刑警寧澤商源,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布车份,位于F島的核電站,受9級(jí)特大地震影響炊汹,放射性物質(zhì)發(fā)生泄漏躬充。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一讨便、第九天 我趴在偏房一處隱蔽的房頂上張望充甚。 院中可真熱鬧,春花似錦霸褒、人聲如沸伴找。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)技矮。三九已至,卻和暖如春殊轴,著一層夾襖步出監(jiān)牢的瞬間衰倦,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工旁理, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留樊零,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓孽文,卻偏偏與公主長(zhǎng)得像驻襟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芋哭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345