概述
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)有greenDAO,LitePal沫浆,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)用了Singer的saveThrows方法严里,繼續(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)用了Singer的onSave方法,傳入了當(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)用了SQLiteDataBase的insert方法
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)似typechange,LitePalBase抠藕,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)汰聋。
其實(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)系
通過(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)系
下面一一對(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)系
通過(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搪锣,Many2OneAnalyzer,Many2ManyAnalyzer彩掐,郭神就差用中文命名了构舟,很好懂了,不再多說(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)系
成員變量
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)系
成員變量
//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
方法概覽
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
方法概覽
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
方法概覽
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
方法概覽
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
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)用了QueryHandler的count方法
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)拴驮。