Architecture(4)LitePal源碼分析

概述

SQLite

在介紹LitePal之前還是要先介紹一下SQLite,也就是我們通常所說的數(shù)據(jù)庫,開發(fā)中多多少少會用到,不過原生的SQLiteDatabase,只要寫過你就知道,寫Demo還是可以的,但是在實(shí)際項(xiàng)目中就不夠靈活了,因?yàn)镴ava作為面向?qū)ο蟮恼Z言,我們在實(shí)際開發(fā)的過程中操作的大部分都是對象,如果使用SQLiteDatabase,我們進(jìn)行CRUD操作的時候需要寫SQL語句,查詢的也是一個Cursor。所以需要根據(jù)就跟網(wǎng)絡(luò)請求一樣,如果是簡單的網(wǎng)絡(luò)請求,你可以用HttpURLConnection或者OKHttpURLConnection,但是真正的項(xiàng)目開發(fā)的時候,也是各種框架用地飛起,所以就有人對DB的CRUD操作進(jìn)行了封裝,于是就產(chǎn)生了很多的ORM框架,LitePal便是其中的一種。

LitePal

LitePal的作者是郭霖,使用方式比較簡潔,從名字來看LitePal比較輕,翻譯過來是,'輕的朋友',GitHub上面有很多的ORM(Object Relational Mapping),也就是通常所說的關(guān)系型數(shù)據(jù)庫框架。常見有greenDAO,LitePalOrmLite等等,下面看一下主流的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ù)來排序的,可以看到LitePal是排第三,F(xiàn)ork數(shù)跟Star數(shù)量基本上是正相關(guān)的,但是由于LitePal推出的比較晚,而且是作者一人在維護(hù)(Contributors數(shù)據(jù)摘自GitHub),所以郭神還是相當(dāng)厲害的。

LitePal的使用很簡單,下面以官方的Sample舉例,可以在asset下的literpal.xml中配置DB的版本以及存儲的數(shù)據(jù)對象,存儲路徑,:

<?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,使用方式比較簡單,并且作者專門開了一個專欄:Android數(shù)據(jù)庫高手秘籍,關(guān)于LItePal的使用可以看一下作者的專欄,還有GitHub上的Sample也很詳細(xì)地介紹了這個框架,下面主要從源碼的角度來解讀一下Litepal的實(shí)現(xiàn)原理,其實(shí)分析起來還是很簡單的,就是注釋有些翻譯起來很痛苦,強(qiáng)烈建議來一個中文版的注釋。

正文

工作流程

Litepal是個ORM框架,所以不像AsncTaskVolley,Picasso那樣流程比較復(fù)雜,以及線程切換等,它的中心在于讓DB操作更加簡單跟高效,基本上跟數(shù)據(jù)庫打過交道都知道,數(shù)據(jù)庫的主要操作就是CRUD,然后稍微麻煩點(diǎn)的就是DB的創(chuàng)建,升級等,說白了就是編寫SQL語句比較麻煩,畢竟做Android客戶端開發(fā)不像后臺天天跟數(shù)據(jù)庫打交道,隨手一個SQL語句信手拈來,LitePal將DB的操作封裝了對象的操作,也就是我們通常所說的ORM操作,這樣操作起來就會比較方便了,我們不需要擔(dān)心SQL語句編寫錯誤,平時怎么操作對象,現(xiàn)在早怎么操作數(shù)據(jù)庫,同時Litepal也保留了原始的SQLite語句查詢方式。

下面從跟隨Sample中的示例代碼,LitePal的Save操作,跟著源碼來追一下Litepal的工作流程

Singer是一個繼承自DataSupport的類,調(diào)用一下save方法,即可觸發(fā)DB的存儲造作,跟一下源碼

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是一個同步方法,在這里通過Connector獲取到SQLiteDatabase的實(shí)例db,然后開啟事務(wù),創(chuàng)建了一個SaveHandler的實(shí)例,并且在構(gòu)造方法中傳入了SQLiteDatabase,調(diào)用了SingeronSave方法,傳入了當(dāng)前對象的實(shí)例,如果執(zhí)行此方法沒有異常,那么就說明存儲成功,關(guān)閉事務(wù),否則會拋出異常,事務(wù)失敗。那么繼續(xù)看SaveHandler中的onSave方法,并且傳入了自身對象,所以繼續(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ù)雜一下,下面通過一行一行的代碼來分析一下

void onSave(DataSupport baseObj) throws SecurityException, IllegalArgumentException,
      NoSuchMethodException, IllegalAccessException, InvocationTargetException {
      //拿到對象的類名
   String className = baseObj.getClassName();
   //根據(jù)className,通過反射獲取到LitePal支持的普通成員變量
   List<Field> supportedFields = getSupportedFields(className);
   //根據(jù)className,通過反射獲取到LitePal支持的泛型變量
   List<Field> supportedGenericFields = getSupportedGenericFields(className);
   //根據(jù)className,通過反射獲得到數(shù)據(jù)庫的映射關(guān)系
   Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);
   if (!baseObj.isSaved()) {
     //通過Id判斷是否是首次存儲
           if (!ignoreAssociations) {
             //看表的映射關(guān)系是否需要處理
               analyzeAssociatedModels(baseObj, associationInfos);
           }
            //存儲該列數(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);
   //對保存的module賦予Id,進(jìn)行加密等操作
   afterSave(baseObj, supportedFields, supportedGenericFields, id);
}

跟一下saving這個方法,發(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);
}

好像整個流程到這里基本上完成了一次保存或者更新的操作,還是比較簡單的。不過上面主要是在分析流程,有很多細(xì)節(jié)沒有深入,涉及到的幾個類有DataSupport,SaveHandler,Connector,由于只是進(jìn)行了保存操作,所以還有很多類沒有涉及到,類似typechange,LitePalBase,AsyncExecutor,DataHandler等,這些會接下來的LitePal架構(gòu)中進(jìn)行具體的分析。

LitePal架構(gòu)

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

Litepal

其實(shí)觀察一下可以發(fā)現(xiàn),crud包跟tablemanager包是整個框架的核心,因?yàn)槠鋵?shí)這兩個包有些東西是有關(guān)聯(lián)的,所以沒法具體的進(jìn)行劃分,所以現(xiàn)在選取了三個抽象類LitePalBase,AsyncExecutorOrmChange因?yàn)榇蟛糠趾诵念惗际抢^承自這三個抽象類的。

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是一個Android庫,開發(fā)者可以用這個庫很容易地操作數(shù)據(jù)庫。你可以通過調(diào)用initialize(Contetext)來初始化LitePal,當(dāng)然你也可以通過調(diào)用use(LitePalDB)來使用指定的數(shù)據(jù)庫或者調(diào)用useDefault來使用litepal.xml中默認(rèn)的數(shù)據(jù)庫。

aesKey

設(shè)置AES加密的key

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

isDefaultDatabase

//判斷數(shù)據(jù)庫是否是默認(rèn)的數(shù)據(jù)庫
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ù)庫版本
private static void removeVersionInSharedPreferences(String dbName) {
    if (isDefaultDatabase(dbName)) {
        SharedUtil.removeVersion(null);
    } else {
        SharedUtil.removeVersion(dbName);
    }
}

LitePalApplication

主要用來給LitePal操作數(shù)據(jù)庫添加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的配置信息,類似于litepal.xml,不過可以動態(tài)地配置DB,在需要添加多個數(shù)據(jù)庫的時候這個功能非常重要

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

成員變量


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

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)自動生成的數(shù)據(jù)庫映射類
        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.

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

繼承關(guān)系

OrmChange

通過注釋,我們知道OrmChange是類型轉(zhuǎn)換的父類,然后有六個子類,也就是數(shù)據(jù)庫支持的存儲類型,BlobOrm是二進(jìn)制,DecimalOrm是浮點(diǎn)數(shù)類型,BooleanOrm是布爾類型,TextOrm是文本類型,DateOrm是日期類型。

成員變量

由于OrmChange的只有一個抽象方法,所以我選擇了其中的一個子類DecimalOrm來分析,比較簡單。

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中有兩個自定義注解,一個是Encrypt,一個是Column。

Encrypt

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

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

}

Cloumn

列注解,主要是用來修飾表中的字段

@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ù)庫時,是否忽略此字段,默認(rèn)值是false
    boolean ignore() default false;
}

Model

實(shí)際上沒有這個類,我為了分析方便,虛擬了這個Model,因?yàn)檫@幾個Module都是在創(chuàng)建表的時候會用到的

繼承關(guān)系

Model

下面一一對這些Module進(jìn)行說明,

ColumnModel

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

這個類主要用于數(shù)據(jù)庫中的列,存儲了列的名稱,類型以及關(guān)于列的一下限制信息

成員變量

private String columnName;//列名
private String columnType;//列的類型
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,存放了表名,列名,列的類型以及id的列名。當(dāng)有泛型的集合在model中聲明的時候,這個類用來創(chuàng)建泛型的表。

成員變量

private String tableName;//表名
private String valueColumnName;//列表
private String valueColumnType;//列的類型
private String valueIdColumnName;//在主表中的引用
private String getMethodName;//查詢的關(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,它主要用來存儲表名,關(guān)聯(lián)的表名,持有外鍵的表名以及關(guān)聯(lián)的類型。表有三種關(guān)聯(lián)關(guān)系:一對一、多對一和多對多。如果關(guān)聯(lián)的類型是一對一或者多對一,外鍵就在在關(guān)聯(lián)表中。如果關(guān)聯(lián)關(guān)系是多對多,一個中間表將會被創(chuàng)建,中間表的名稱是相互關(guān)聯(liá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)類型

所有的方法都是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,存儲了表名以及存放了所有列的HashMap

這個之前可能用的是HashMap來存儲的,不過新版中是采用List來進(jìn)行 存儲的。

成員變量

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

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

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組件的基類。如果一個組件需要跟其他的組件進(jìn)行交互,或者由于他們有相同的邏輯以至于導(dǎo)致了重復(fù)的代碼,LitePalBase可能是一個很好的解決方案。

繼承關(guān)系

LitePalBase

通過類圖可以看到,LitePal的直接子類有兩個:

  • Generator:制造者,那么就是操作表,最終的子類一個是Dropper:刪除表,一個是Upgrader:升級表。
  • DataHandler:數(shù)據(jù)處理者,表中的數(shù)據(jù)處理最常見的就是CRUD操作,確實(shí)他有五個子類,UpdateHandler用來更新表中的數(shù)據(jù),SaveHandler用來保存表中的數(shù)據(jù),QueryHandler用來查詢表中的數(shù)據(jù),DeleteHandler則用來刪除表中數(shù)據(jù),還有一個是AssociationsAnalyzer,關(guān)系分析者,用來處理表之間的映射關(guān)系的,他的子類的有三個,One2OneAnalyzerMany2OneAnalyzer,Many2ManyAnalyzer,郭神就差用中文命名了,很好懂了,不再多說。

LitePalBase

成員變量
public static final String TAG = "LitePalBase";
//Action標(biāo)志,獲取表的映射關(guān)系種類
private static final int GET_ASSOCIATIONS_ACTION = 1;
//Action標(biāo)志,獲取表的具體映射關(guān)系
private static final int GET_ASSOCIATION_INFO_ACTION = 2;
//對象類型映射的類型數(shù)組
private OrmChange[] typeChangeRules = { new NumericOrm(), new TextOrm(), new BooleanOrm(),
      new DecimalOrm(), new DateOrm(), new BlobOrm()};
//key為對象的類名,value是DB支持的普通成員變量集合
private Map<String, List<Field>> classFieldsMap = new HashMap<>();
//key為對象的類名,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;

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

convertFieldToColumnModel

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

private ColumnModel convertFieldToColumnModel(Field field) {
    //獲取變量類型
    String fieldType = field.getType().getName();
    //根據(jù)類型匹配在表中對應(yīng)的類型
    String columnType = getColumnType(fieldType);
    boolean nullable = true;
    boolean unique = false;
    String defaultValue = "";
    Column annotation = field.getAnnotation(Column.class);
   //通過運(yùn)行時注解給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)系

看源碼的時候感覺這個地方是最難理解的。簡單來說關(guān)聯(lián)關(guān)系其實(shí)有三種,一對一,多對一,多對多。還有就是單向關(guān)聯(lián)跟雙向關(guān)聯(lián),所以實(shí)際上一共有6種關(guān)聯(lián)關(guān)系。在LitePal中,是通過對象跟集合來區(qū)分的。下面舉例說明

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

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

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

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

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

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

    ?

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

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

    ?

接下來的兩個方法,在理解了上面的概念之后才能更好地理解,不然很難理解

oneToAnyConditions
private void oneToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
    //獲取成員變量的類型
    Class<?> fieldTypeClass = field.getType();
    // 判斷l(xiāng)itepal.xml映射的類中是否包含fieldTypeClass
    if (LitePalAttr.getInstance().getClassNames().contains(fieldTypeClass.getName())) {
        //通過反射創(chuàng)建映射的關(guān)系表的類
        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)系是一對一
                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類中含有泛型集合,說明關(guān)聯(lián)關(guān)系是多對一
                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,說明是單向關(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中沒有集合類的成員變量,那么肯定不是多對多
    if (isCollection(field.getType())) {
        String genericTypeName = getGenericTypeName(field);
        //A類中包含泛型集合類
        if (LitePalAttr.getInstance().getClassNames().contains(genericTypeName)) {
            //通過反射創(chuàng)建一個B類
            Class<?> reverseDynamicClass = Class.forName(genericTypeName);
            //拿到B類的所有成員變量
            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,說明是多對一
                    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的成員變量有集合類
                    else if (isCollection(reverseFieldTypeClass)) {
                        String reverseGenericTypeName = getGenericTypeName(reverseField);
                       //如果B中的泛型跟A相同,那么就是多對多
                        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),作為多對一處理
            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)系類型的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

通過SQL語句建表

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)換成小寫
                       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í)例用來進(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);
     //通過參數(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")));
           //將查詢出來的Value給Module賦值
            setValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);
           //將泛型的Value賦值給Module
            setGenericValueToModel((DataSupport) modelInstance, supportedGenericFields, genericModelMap);
           //設(shè)置關(guān)聯(lián)對象的值
            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

對關(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)系為多對一或者一對一
      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) {
        //多對多
         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的子類實(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 {
   //獲取類名
   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);
   //是否存儲過
   if (!baseObj.isSaved()) {
           if (!ignoreAssociations) {
               analyzeAssociatedModels(baseObj, associationInfos);
           }
            //進(jìn)行數(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 {
   //清空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

找到第一個

<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)表查詢
            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

如果只是對少量的數(shù)據(jù)進(jìn)行操作,實(shí)際上在主線程中操作是沒有問題的,但是如果是大量的數(shù)據(jù),那么就會比較耗時,所以就必須在子線程中進(jìn)行,所以LitePal支持在子線程中進(jìn)行DB操作。

繼承關(guān)系

AsyncExecutor
AsyncExecutor
public abstract class AsyncExecutor {

    //后臺Runnable
    private Runnable pendingTask;
   //提交一個任務(wù)
    public void submit(Runnable task) {
        pendingTask = task;
    }
   //開啟線程
    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;
   //開啟線程
    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() {
                           //主線程中回調(diào)
                            executor.getListener().onFinish(average);
                        }
                    });
                }
            }
        }
    };
    executor.submit(runnable);
    return executor;
}

另外幾個Executor實(shí)際上也是這么使用的,也就是在子線程中進(jìn)行查詢完成,然后通過Handler將結(jié)果以post Runnable的形式傳遞到主線程,通過接口回調(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將類連接到SQLite數(shù)據(jù)庫的表以建立一個應(yīng)用程序幾乎零配置的持久層。在應(yīng)用的Context中,這些類被定義為Module,Module也可以跟別的Modules產(chǎn)生聯(lián)系。

DataSupport嚴(yán)重依賴于它使用類和關(guān)聯(lián)的命名。在數(shù)據(jù)庫表和外部表之間建立映射的名稱,外鍵。

在類和表、屬性和列之間的自動映射。

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是所有映射的對象的父類,定義了所有的關(guān)于DB的操作方法,并且每個操作都有同步跟異步方法,異步方法最終還是會調(diào)用同步方法,只是在調(diào)用的時候加了一個同步鎖,有點(diǎn)類似Picasso的單例,可以是單個對象也就是某一列的操作,也可以是對整個DB的操作,如果是基本的CRUD操作,那么DataSupport會直接調(diào)用DataHandler的相應(yīng)的子類的相應(yīng)的方法去執(zhí)行。

聚合查詢

如果是聚合查詢,也就是說需要多個條件的查詢,LitePal提供了一個類ClusterQuery,可以設(shè)置多個查詢條件,并且采用了Builder設(shè)計(jì)模式可以動態(tài)的設(shè)置查詢條件,類似average,count,limit等操作,下面會重點(diǎn)分析一下這個類。

ClusterQuery

注釋

Allows developers to query tables with cluster style.

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

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

成員變量

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

構(gòu)造方法

ClusterQuery() {
}

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

Builder


//設(shè)置要查詢的列
public ClusterQuery select(String... columns) {
    mColumns = columns;
    return this;
}
//設(shè)置查詢條件
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模式,可以動態(tài)地進(jìn)行添加參數(shù),也就是實(shí)現(xiàn)連綴查詢,查詢起來比較方便。

count&countAsync

public CountExecutor countAsync(final String tableName) {
    final CountExecutor executor = new CountExecutor();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (DataSupport.class) {
              //同步獲取查詢結(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ù)
    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) {
              //同步獲取最小值的查詢結(jié)果
                final T t = min(tableName, columnName, columnType);
                if (executor.getListener() != null) {
                    LitePal.getHandler().post(new Runnable() {
                        @Override
                        public void run() {
                          //回調(diào)通知主線程
                            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ù)
    return queryHandler.onMin(tableName, columnName, mConditions, columnType);
}

Utils

CipherUtil

這個主要用來進(jìn)行加解密的,LitePal采用的是AES加密算法,而且加密的key可以在LitePal中進(jìn)行動態(tài)設(shè)置,不過也支持MD5加密,不過MD5無法解密

//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

基本的工具類,封裝了一些最基本的方法

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

DBUtility

  • getTableNameByClassName:通過類名獲取表名
  • isTableExists:查詢表是否存在

SharedUtil

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

總結(jié)

LitePal利用反射對獲取Model的屬性,然后拼接成SQL語句,對于調(diào)用者來說減少了很多重復(fù)性操作,升級也比較方便,符合Java的面向?qū)ο?,使用者不用再關(guān)心底層的SQL語句,當(dāng)然LitePal同時也提供了使用SQL語句查詢的功能,使用者可以憑喜好使用,看了SQL的源碼實(shí)現(xiàn)之后,個人覺得有些地方可以改進(jìn)。

反射優(yōu)化

LitePal的CRUD操作每次都需要反射,這些在數(shù)據(jù)量少的時候影響還好,在數(shù)據(jù)量較大的時候會比較影響效率,可以對已經(jīng)反射過的Model屬性緩存起來,那么下一次CRUD操作的時候如果是同一個Model,那么直接從緩存中取,那么效率就會快一些。

同步優(yōu)化

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

單例優(yōu)化

LitePal的每一次CRUD操作,都需要對DataHandler的子類SaveHandler、QueryHandler進(jìn)行一次實(shí)例化,如果采用單例結(jié)合Builder模式的話可以減少一部分開銷,還有ClusterQuery也是如此。

線程優(yōu)化

LitePal開啟多線程的時候是采用的new Thread方式,如果有多個異步的DB操作的時候,會創(chuàng)建很多個線程,如果使用線程池可以減少一部分開銷。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,334評論 2 377