概述
SQLite
在介绍LitePal之前还是要先介绍一下SQLite,也就是我们通常所说的数据库,开发中多多少少会用到,不过原生的SQLiteDatabase,只要写过你就知道,写Demo还是可以的,但是在实际项目中就不够灵活了,因为Java作为面向对象的语言,我们在实际开发的过程中操作的大部分都是对象,如果使用SQLiteDatabase,我们进行CRUD操作的时候需要写SQL语句,查询的也是一个Cursor。所以需要根据就跟网络请求一样,如果是简单的网络请求,你可以用HttpURLConnection或者OKHttpURLConnection,但是真正的项目开发的时候,也是各种框架用地飞起,所以就有人对DB的CRUD操作进行了封装,于是就产生了很多的ORM框架,LitePal便是其中的一种。
LitePal
LitePal的作者是郭霖,使用方式比较简洁,从名字来看LitePal比较轻,翻译过来是,'轻的朋友',GitHub上面有很多的ORM(Object Relational Mapping),也就是通常所说的关系型数据库框架。常见有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数来排序的,可以看到LitePal是排第三,Fork数跟Star数量基本上是正相关的,但是由于LitePal推出的比较晚,而且是作者一人在维护(Contributors数据摘自GitHub),所以郭神还是相当厉害的。
LitePal的使用很简单,下面以官方的Sample举例,可以在asset下的literpal.xml中配置DB的版本以及存储的数据对象,存储路径,:
本文分析的是GitHub上LitePal的最新版本1.6.1,使用方式比较简单,并且作者专门开了一个专栏:Android数据库高手秘籍,关于LItePal的使用可以看一下作者的专栏,还有GitHub上的Sample也很详细地介绍了这个框架,下面主要从源码的角度来解读一下Litepal的实现原理,其实分析起来还是很简单的,就是注释有些翻译起来很痛苦,强烈建议来一个中文版的注释。
正文
工作流程
Litepal是个ORM框架,所以不像AsncTask,Volley,Picasso那样流程比较复杂,以及线程切换等,它的中心在于让DB操作更加简单跟高效,基本上跟数据库打过交道都知道,数据库的主要操作就是CRUD,然后稍微麻烦点的就是DB的创建,升级等,说白了就是编写SQL语句比较麻烦,毕竟做Android客户端开发不像后台天天跟数据库打交道,随手一个SQL语句信手拈来,LitePal将DB的操作封装了对象的操作,也就是我们通常所说的ORM操作,这样操作起来就会比较方便了,我们不需要担心SQL语句编写错误,平时怎么操作对象,现在早怎么操作数据库,同时Litepal也保留了原始的SQLite语句查询方式。
下面从跟随Sample中的示例代码,LitePal的Save操作,跟着源码来追一下Litepal的工作流程
Singer是一个继承自DataSupport的类,调用一下save方法,即可触发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();
依然是方法调用,调用了Singer的saveThrows方法,继续跟
public synchronized boolean save() {
try {
saveThrows();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
saveThrows是一个同步方法,在这里通过Connector获取到SQLiteDatabase的实例db,然后开启事务,创建了一个SaveHandler的实例,并且在构造方法中传入了SQLiteDatabase,调用了Singer的onSave方法,传入了当前对象的实例,如果执行此方法没有异常,那么就说明存储成功,关闭事务,否则会抛出异常,事务失败。那么继续看SaveHandler中的onSave方法,并且传入了自身对象,所以继续跟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方法,逻辑稍微复杂一下,下面通过一行一行的代码来分析一下
void onSave(DataSupport baseObj) throws SecurityException, IllegalArgumentException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//拿到对象的类名
String className = baseObj.getClassName();
//根据className,通过反射获取到LitePal支持的普通成员变量
List supportedFields = getSupportedFields(className);
//根据className,通过反射获取到LitePal支持的泛型变量
List supportedGenericFields = getSupportedGenericFields(className);
//根据className,通过反射获得到数据库的映射关系
Collection associationInfos = getAssociationInfo(className);
if (!baseObj.isSaved()) {
//通过Id判断是否是首次存储
if (!ignoreAssociations) {
//看表的映射关系是否需要处理
analyzeAssociatedModels(baseObj, associationInfos);
}
//存储该列数据
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 supportedFields, List supportedGenericFields)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
values.clear();//清空上一次CRUD操作的ContentValue
//将Singer中的数据转换成ContentValues
beforeSave(baseObj, supportedFields, values);
//保存数据,并且获取Id
long id = saving(baseObj, values);
//对保存的module赋予Id,进行加密等操作
afterSave(baseObj, supportedFields, supportedGenericFields, id);
}
跟一下saving这个方法,发现已经到头了,直接调用了SQLiteDataBase的insert方法
private long saving(DataSupport baseObj, ContentValues values) {
if (values.size() == 0) {
values.putNull("id");
}
return mDatabase.insert(baseObj.getTableName(), null, values);
}
好像整个流程到这里基本上完成了一次保存或者更新的操作,还是比较简单的。不过上面主要是在分析流程,有很多细节没有深入,涉及到的几个类有DataSupport,SaveHandler,Connector,由于只是进行了保存操作,所以还有很多类没有涉及到,类似typechange,LitePalBase,AsyncExecutor,DataHandler等,这些会接下来的LitePal架构中进行具体的分析。
LitePal架构
由于LitePal分了很多包,而且是通过功能进行划分的,为了便于直观的展示,我将包转化成了思维导图,这样可以更加直观地了解整个LitePal的架构。
其实观察一下可以发现,crud包跟tablemanager包是整个框架的核心,因为其实这两个包有些东西是有关联的,所以没法具体的进行划分,所以现在选取了三个抽象类LitePalBase,AsyncExecutor,OrmChange因为大部分核心类都是继承自这三个抽象类的。
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库,开发者可以用这个库很容易地操作数据库。你可以通过调用initialize(Contetext)来初始化LitePal,当然你也可以通过调用use(LitePalDB)来使用指定的数据库或者调用useDefault来使用litepal.xml中默认的数据库。
aesKey
设置AES加密的key
public static void aesKey(String key) {
CipherUtil.aesKey = key;
}
isDefaultDatabase
//判断数据库是否是默认的数据库
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中指定的数据库版本
private static void removeVersionInSharedPreferences(String dbName) {
if (isDefaultDatabase(dbName)) {
SharedUtil.removeVersion(null);
} else {
SharedUtil.removeVersion(dbName);
}
}
LitePalApplication
主要用来给LitePal操作数据库添加Context
public LitePalApplication() {
sContext = this;
}
//已经被遗弃
@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,不过可以动态地配置DB,在需要添加多个数据库的时候这个功能非常重要
通过litepal.xml配置的数据库的时候只能添加一个,通过LitePalDB可以支持多个数据库。
成员变量
private int version;//版本号
private String dbName;//DB名称
private String storage;//存储路径
private boolean isExternalStorage = false;//外部存储是否又读取权限
private List classNames;//映射的类名集合
fromDefault
//获取默认的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 getClassNames() {
if (classNames == null) {
classNames = new ArrayList();
//添加系统自动生成的数据库映射类
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.
一个用于将对象的成员变量属性映射成数据库中表的列的属性的抽象父类。这个类的目的在于定义一个抽象方法,并且让子类去实现他。每一个子类处理一种变换并且每个子类各司其职。
继承关系
通过注释,我们知道OrmChange是类型转换的父类,然后有六个子类,也就是数据库支持的存储类型,BlobOrm是二进制,DecimalOrm是浮点数类型,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
运行时注解,提供加密算法
@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 {
//变量的值属性是否能为空,默认值为true
boolean nullable() default true;
//字段是否唯一,默认值为false
boolean unique() default false;
//字段的默认值,默认值为“”
String defaultValue() default "";
//映射到数据库时,是否忽略此字段,默认值是false
boolean ignore() default false;
}
Model
实际上没有这个类,我为了分析方便,虚拟了这个Model,因为这几个Module都是在创建表的时候会用到的
继承关系
下面一一对这些Module进行说明,
ColumnModel
注释
This is a model class for columns. It stores column name, column type, and column constraints information.
这个类主要用于数据库中的列,存储了列的名称,类型以及关于列的一下限制信息
成员变量
private String columnName;//列名
private String columnType;//列的类型
private boolean isNullable = true;//是否可以为null
private boolean isUnique = false;//是否具有唯一性
private String defaultValue = "";//默认值
主要方法
//设置默认值
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的列名。当有泛型的集合在model中声明的时候,这个类用来创建泛型的表。
成员变量
private String tableName;//表名
private String valueColumnName;//列表
private String valueColumnType;//列的类型
private String valueIdColumnName;//在主表中的引用
private String getMethodName;//查询的关联的表名
所有的方法都是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.
表关系的model,它主要用来存储表名,关联的表名,持有外键的表名以及关联的类型。表有三种关联关系:一对一、多对一和多对多。如果关联的类型是一对一或者多对一,外键就在在关联表中。如果关联关系是多对多,一个中间表将会被创建,中间表的名称是相互关联的两个表的名称中间加上下划线,关联表的排列顺序是按照首字母的的顺序进行排列。
成员变量
private String tableName;//表名
private String associatedTableName;//关联的表名
private String tableHoldsForeignKey;//持有外键的表
private int associationType;//关联类型
所有的方法都是Get,Set方法,不再赘述。
TableModel
注释
This is a model class for tables. It stores a table name and a HashMap for
columns in the table.
表相关的model,存储了表名以及存放了所有列的HashMap
这个之前可能用的是HashMap来存储的,不过新版中是采用List来进行 存储的。
成员变量
private String tableName;//表名
//存放列的Module的集合
private List columnModels = new ArrayList();
private String className;//映射的对象名称
getColumnModelByName
根据名称返回指定的列
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
根据列名删除某一列
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组件的基类。如果一个组件需要跟其他的组件进行交互,或者由于他们有相同的逻辑以至于导致了重复的代码,LitePalBase可能是一个很好的解决方案。
继承关系
通过类图可以看到,LitePal的直接子类有两个:
- Generator:制造者,那么就是操作表,最终的子类一个是Dropper:删除表,一个是Upgrader:升级表。
- DataHandler:数据处理者,表中的数据处理最常见的就是CRUD操作,确实他有五个子类,UpdateHandler用来更新表中的数据,SaveHandler用来保存表中的数据,QueryHandler用来查询表中的数据,DeleteHandler则用来删除表中数据,还有一个是AssociationsAnalyzer,关系分析者,用来处理表之间的映射关系的,他的子类的有三个,One2OneAnalyzer,Many2OneAnalyzer,Many2ManyAnalyzer,郭神就差用中文命名了,很好懂了,不再多说。
LitePalBase
成员变量
public static final String TAG = "LitePalBase";
//Action标志,获取表的映射关系种类
private static final int GET_ASSOCIATIONS_ACTION = 1;
//Action标志,获取表的具体映射关系
private static final int GET_ASSOCIATION_INFO_ACTION = 2;
//对象类型映射的类型数组
private OrmChange[] typeChangeRules = { new NumericOrm(), new TextOrm(), new BooleanOrm(),
new DecimalOrm(), new DateOrm(), new BlobOrm()};
//key为对象的类名,value是DB支持的普通成员变量集合
private Map> classFieldsMap = new HashMap<>();
//key为对象的类名,value是DB支持泛型成员变量集合
private Map> classGenericFieldsMap = new HashMap<>();
// AssociationsModel的集合
private Collection mAssociationModels;
//association info的Collection
private Collection mAssociationInfos;
//GenericModel models的Collection
private Collection mGenericModels;
通过成员变量,能够看出LitePalBase作为父类主要是通过反射把Singer中的成员变量转换成对应的AssociationsModel,GenericModel,以及ColumnModule的集合或者Map,封装了一些获取这些值的方法,供他的子类在表结构更改,单个表的增删改查中进行使用。
convertFieldToColumnModel
将对象中DB支持的字段转化成对应的Model
private ColumnModel convertFieldToColumnModel(Field field) {
//获取变量类型
String fieldType = field.getType().getName();
//根据类型匹配在表中对应的类型
String columnType = getColumnType(fieldType);
boolean nullable = true;
boolean unique = false;
String defaultValue = "";
Column annotation = field.getAnnotation(Column.class);
//通过运行时注解给ColumnModel设值
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;
}
关联关系
看源码的时候感觉这个地方是最难理解的。简单来说关联关系其实有三种,一对一,多对一,多对多。还有就是单向关联跟双向关联,所以实际上一共有6种关联关系。在LitePal中,是通过对象跟集合来区分的。下面举例说明
假设现在有两个类,A跟B,都继承自DataSupport
- 一对一单向关联:A中包含B,但是B中不包含A
public class A extends DataSupport {
private B b;
}
public class B extends DataSupport{
}
- 一对一双向关联:A中包含B,同时B中也包含A
public class A extends DataSupport {
private B b;
}
public class B extends DataSupport{
private A a;
}
public class A extends DataSupport {
private List list;
}
public class B extends DataSupport {
}
public class A extends DataSupport {
private List list;
}
public class B extends DataSupport {
private A a;
}
-
多对多单向关联
public class A extends DataSupport { private List list; } public class B extends DataSupport { private List list; }
-
多对多单向关联
public class A extends DataSupport { private List list; } public class B extends DataSupport { private List list; }
接下来的两个方法,在理解了上面的概念之后才能更好地理解,不然很难理解
oneToAnyConditions
private void oneToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
//获取成员变量的类型
Class> fieldTypeClass = field.getType();
// 判断litepal.xml映射的类中是否包含fieldTypeClass
if (LitePalAttr.getInstance().getClassNames().contains(fieldTypeClass.getName())) {
//通过反射创建映射的关系表的类
Class> reverseDynamicClass = Class.forName(fieldTypeClass.getName());
//获取所有的成员变量
Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
//是否是双向关联的标志,默认为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,关联关系是一对一
if (className.equals(reverseFieldTypeClass.getName())) {
if (action == GET_ASSOCIATIONS_ACTION) {
//将AssociationModel添加进Collection
addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
//将AssociationInfo添加进Collection
addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
fieldTypeClass.getName(), field, reverseField, Const.Model.ONE_TO_ONE);
}
//双向关联的标志置为true
reverseAssociations = true;
}
//如果在B类中含有泛型集合,说明关联关系是多对一
else if (isCollection(reverseFieldTypeClass)) {
String genericTypeName = getGenericTypeName(reverseField);
if (className.equals(genericTypeName)) {
if (action == GET_ASSOCIATIONS_ACTION) {
//将AssociationModel添加进Collection
addIntoAssociationModelCollection(className, fieldTypeClass.getName(), className, Const.Model.MANY_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
//将AssociationInfo添加进Collection
addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),className, field, reverseField, Const.Model.MANY_TO_ONE);
}
//双向关联的标志置为true
reverseAssociations = true;
}
}
}
}
//reverseAssociations为false,说明是单向关联
//单向关联
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)) {
//通过反射创建一个B类
Class> reverseDynamicClass = Class.forName(genericTypeName);
//拿到B类的所有成员变量
Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
//双向关联的标志
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添加进Collection
addIntoAssociationModelCollection(className, genericTypeName,
genericTypeName, Const.Model.MANY_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
//将AssociationInfo添加进Collection
addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,field, reverseField, Const.Model.MANY_TO_ONE);
}
//双向关联的标志置为true
reverseAssociations = true;
}
//如果B的成员变量有集合类
else if (isCollection(reverseFieldTypeClass)) {
String reverseGenericTypeName = getGenericTypeName(reverseField);
//如果B中的泛型跟A相同,那么就是多对多
if (className.equals(reverseGenericTypeName)) {
if (action == GET_ASSOCIATIONS_ACTION) {
//将AssociationModel添加进Collection
addIntoAssociationModelCollection(className, genericTypeName, null,Const.Model.MANY_TO_MANY);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
//将AssociationInfo添加进Collection
addIntoAssociationInfoCollection(className, genericTypeName, null, field,reverseField, Const.Model.MANY_TO_MANY);
}
//双向关联的标志置为true
reverseAssociations = true;
}
}
}
}
//非双向关联,作为多对一处理
if (!reverseAssociations) {
if (action == GET_ASSOCIATIONS_ACTION) {
//将AssociationModel添加进Collection
addIntoAssociationModelCollection(className, genericTypeName,
genericTypeName, Const.Model.MANY_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
//将AssociationInfo添加进Collection
addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName, field, null, Const.Model.MANY_TO_ONE);
}
}
} else if (BaseUtility.isGenericTypeSupported(genericTypeName) && action == GET_ASSOCIATIONS_ACTION) {
//如果是双向关联,并且是获取关联关系类型的Action下
Column annotation = field.getAnnotation(Column.class);
//变量是有效的
if (annotation != null && annotation.ignore()) {
return;
}
//创建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
继承关系
成员变量
private Collection mTableModels;//表的Module集合
private Collection mAllRelationModels;//关系Module集合
execute
通过SQL语句建表
protected void execute(List sqls, SQLiteDatabase db) {
String throwSQL = "";
try {
if (sqls != null && !sqls.isEmpty()) {
for (String sql : sqls) {
if (!TextUtils.isEmpty(sql)) {
//转换成小写
throwSQL = BaseUtility.changeCase(sql);
db.execSQL(throwSQL);
}
}
}
} catch (SQLException e) {
throw new DatabaseGenerateException(DatabaseGenerateException.SQL_ERROR + throwSQL);
}
}
create
调用Creator的方法创建或更新表
private static void create(SQLiteDatabase db, boolean force) {
Creator creator = new Creator();
creator.createOrUpgradeTable(db, force);
}
drop
调用Dropper的方法删除表
private static void drop(SQLiteDatabase db) {
Dropper dropper = new Dropper();
dropper.createOrUpgradeTable(db, false);
}
upgrade
调用Upgrader的方法
private static void upgradeTables(SQLiteDatabase db) {
Upgrader upgrader = new Upgrader();
upgrader.createOrUpgradeTable(db, false);
}
DataHandler
继承关系
成员变量
//DB的实例用来进行CRUD操作
SQLiteDatabase mDatabase;
//空的DataSupport实例
private DataSupport tempEmptyModel;
//ModuleA中的AssociationsInfo集合
private List fkInCurrentModel;
//ModuleB中的AssociationsInfo集合
private List fkInOtherModel;
query
protected List query(Class modelClass, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having, String orderBy, String limit,
List foreignKeyAssociations) {
List dataList = new ArrayList();
Cursor cursor = null;
try {
List supportedFields = getSupportedFields(modelClass.getName());
List supportedGenericFields =getSupportedGenericFields(modelClass.getName());
String[] customizedColumns = DBUtility.convertSelectClauseToValidNames(getCustomizedColumns(columns, supportedGenericFields, foreignKeyAssociations));
//拿到table的名字
String tableName = getTableName(modelClass);
//通过参数进行查找
cursor = mDatabase.query(tableName, customizedColumns, selection, selectionArgs,
groupBy, having, orderBy, limit);
if (cursor.moveToFirst()) {
SparseArray queryInfoCacheSparseArray = new SparseArray();
Map genericModelMap = new HashMap();
do {
//创建泛型实例
T modelInstance = (T) createInstanceFromClass(modelClass);
//进行Id赋值
giveBaseObjIdValue((DataSupport) modelInstance,
cursor.getLong(cursor.getColumnIndexOrThrow("id")));
//将查询出来的Value给Module赋值
setValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);
//将泛型的Value赋值给Module
setGenericValueToModel((DataSupport) modelInstance, supportedGenericFields, genericModelMap);
//设置关联对象的值
if (foreignKeyAssociations != null) {
setAssociatedModel((DataSupport) modelInstance);
}
//添加元素进集合
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
对关联关系进行分析
private void analyzeAssociations(String className) {
Collection associationInfos = getAssociationInfo(className);
if (fkInCurrentModel == null) {
fkInCurrentModel = new ArrayList();
} else {
fkInCurrentModel.clear();
}
if (fkInOtherModel == null) {
fkInOtherModel = new ArrayList();
} else {
fkInOtherModel.clear();
}
for (AssociationsInfo associationInfo : associationInfos) {
//关联关系为多对一或者一对一
if (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE
|| associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {
if (associationInfo.getClassHoldsForeignKey().equals(className)) {
//当前module
fkInCurrentModel.add(associationInfo);
} else {
//关联Module
fkInOtherModel.add(associationInfo);
}
} else if (associationInfo.getAssociationType() == Const.Model.MANY_TO_MANY) {
//多对多
fkInOtherModel.add(associationInfo);
}
}
}
analyzeAssociatedModels
protected void analyzeAssociatedModels(DataSupport baseObj, Collection 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的子类实际上是在处理AssociationsInfo,就是将AssociationsInfo分配给当前的Module或者是关联的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 {
//获取类名
String className = baseObj.getClassName();
//获取DB支持的成员变量
List supportedFields = getSupportedFields(className);
//获取DB支持的泛型变量
List supportedGenericFields = getSupportedGenericFields(className);
//获取所有的关联关系
Collection associationInfos = getAssociationInfo(className);
//是否存储过
if (!baseObj.isSaved()) {
if (!ignoreAssociations) {
analyzeAssociatedModels(baseObj, associationInfos);
}
//进行数据存储
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 supportedFields, List supportedGenericFields)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
//清空ContentValues
values.clear();
//将成员变量转化成Values
beforeSave(baseObj, supportedFields, values);
//进行插入操作,拿到返回的ID
long id = saving(baseObj, values);
//如果有关联表,则进行关联表的插入
afterSave(baseObj, supportedFields, supportedGenericFields, id);
}
doUpdateAction
private void doUpdateAction(DataSupport baseObj, List supportedFields, List supportedGenericFields)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
//清空ContentValues
values.clear();
//将成员变量转化成Values
beforeUpdate(baseObj, supportedFields, values);
//进行更新操作
updating(baseObj, values);
//进行关联表操作
afterUpdate(baseObj, supportedGenericFields);
}
QueryHandler
方法概览
onFindFirst
找到第一个
T onFindFirst(Class modelClass, boolean isEager) {
List 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
List onFindAll(Class modelClass, boolean isEager, long... ids) {
List 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 supportedFields = getSupportedFields(baseObj.getClassName());
List supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());
updateGenericTables(baseObj, supportedGenericFields, id);
ContentValues values = new ContentValues();
//转换成员宾亮
putFieldsValue(baseObj, supportedFields, values);
//设置默认值
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 associationInfos = getAssociationInfo(baseObj
.getClassName());
//关联表查询
analyzeAssociatedModels(baseObj, associationInfos);
} catch (Exception e) {
throw new DataSupportException(e.getMessage(), e);
}
}
DeleteHandler
方法概览
onDelete
int onDelete(Class> modelClass, long id) {
//获取泛型变量集合
List supportedGenericFields = getSupportedGenericFields(modelClass.getName());
//删除关联表
deleteGenericData(modelClass, supportedGenericFields, id);
//分析关联表
analyzeAssociations(modelClass);
int rowsAffected = deleteCascade(modelClass, id);
rowsAffected += mDatabase.delete(getTableName(modelClass),"id = " + id, null);
//删除关联表中的数据
getForeignKeyTableToDelete().clear();
return rowsAffected;
}
AsyncExecutor
如果只是对少量的数据进行操作,实际上在主线程中操作是没有问题的,但是如果是大量的数据,那么就会比较耗时,所以就必须在子线程中进行,所以LitePal支持在子线程中进行DB操作。
继承关系
AsyncExecutor
public abstract class AsyncExecutor {
//后台Runnable
private Runnable pendingTask;
//提交一个任务
public void submit(Runnable task) {
pendingTask = task;
}
//开启线程
void execute() {
if (pendingTask != null) {
new Thread(pendingTask).start();
}
}
}
AverageExecutor
//回调的接口
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() {
//主线程中回调
executor.getListener().onFinish(average);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
另外几个Executor实际上也是这么使用的,也就是在子线程中进行查询完成,然后通过Handler将结果以post Runnable的形式传递到主线程,通过接口回调传递操作的结果。
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数据库的表以建立一个应用程序几乎零配置的持久层。在应用的Context中,这些类被定义为Module,Module也可以跟别的Modules产生联系。
DataSupport严重依赖于它使用类和关联的命名。在数据库表和外部表之间建立映射的名称,外键。
在类和表、属性和列之间的自动映射。
delete&deleteAsync
public static synchronized int delete(Class> modelClass, long id) {
int rowsAffected = 0;
SQLiteDatabase db = Connector.getDatabase();
db.beginTransaction();
try {
//调用了DeleteHandler
DeleteHandler deleteHandler = new DeleteHandler(db);
rowsAffected = deleteHandler.onDelete(modelClass, id);
db.setTransactionSuccessful();
return rowsAffected;
} finally {
db.endTransaction();
}
}
saveAll&saveAllAsync
public static synchronized void saveAll(Collection collection) {
SQLiteDatabase db = Connector.getDatabase();
db.beginTransaction();
try {
//调用了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 {
//调用了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);
}
接着调用了ClusterQuery的average方法,ClusterQuery然后调用了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 max(String tableName, String columnName, Class columnType) {
ClusterQuery cQuery = new ClusterQuery();
return cQuery.max(tableName, columnName, columnType);
}
接着调用了ClusterQuery的max方法,ClusterQuery然后调用了max的方法
public synchronized T max(String tableName, String columnName, Class columnType) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
return queryHandler.onMax(tableName, columnName, mConditions, columnType);
}
CRUD操作
DataSupport是所有映射的对象的父类,定义了所有的关于DB的操作方法,并且每个操作都有同步跟异步方法,异步方法最终还是会调用同步方法,只是在调用的时候加了一个同步锁,有点类似Picasso的单例,可以是单个对象也就是某一列的操作,也可以是对整个DB的操作,如果是基本的CRUD操作,那么DataSupport会直接调用DataHandler的相应的子类的相应的方法去执行。
聚合查询
如果是聚合查询,也就是说需要多个条件的查询,LitePal提供了一个类ClusterQuery,可以设置多个查询条件,并且采用了Builder设计模式可以动态的设置查询条件,类似average,count,limit等操作,下面会重点分析一下这个类。
ClusterQuery
注释
Allows developers to query tables with cluster style.
让开发者能够以聚集的风格进行查询。
这段确实不知道怎么翻译,不过意思就是能够进行聚合查询。
成员变量
String[] mColumns;//查询的列
String[] mConditions;//查询条件
String mOrderBy;//排序条件
String mLimit;//返回数据的数量
String mOffset;//偏移量,也就是第几行开始查询
构造方法
ClusterQuery() {
}
空方法,可以让外部实例化
Builder
//设置要查询的列
public ClusterQuery select(String... columns) {
mColumns = columns;
return this;
}
//设置查询条件
public ClusterQuery where(String... conditions) {
mConditions = conditions;
return this;
}
//设置排序准则
public ClusterQuery order(String column) {
mOrderBy = column;
return this;
}
//设置返回数据的数量
public ClusterQuery limit(int value) {
mLimit = String.valueOf(value);
return this;
}
//设置偏移量
public ClusterQuery offset(int value) {
mOffset = String.valueOf(value);
return this;
}
ClusterQuery采用了Builder模式,可以动态地进行添加参数,也就是实现连缀查询,查询起来比较方便。
count&countAsync
public CountExecutor countAsync(final String tableName) {
final CountExecutor executor = new CountExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
//同步获取查询结果
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方法,调用了QueryHandler的count方法
public synchronized int count(String tableName) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
//然后添加了构造时候的参数
return queryHandler.onCount(tableName, mConditions);
}
min&minAsync
public FindExecutor minAsync(final String tableName, final String columnName, final Class columnType) {
final FindExecutor executor = new FindExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
//同步获取最小值的查询结果
final T t = min(tableName, columnName, columnType);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
//回调通知主线程
executor.getListener().onFinish(t);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
追一下min方法
public synchronized T min(String tableName, String columnName, Class columnType) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
//然后添加了构造时候的查询参数
return queryHandler.onMin(tableName, columnName, mConditions, columnType);
}
Utils
CipherUtil
这个主要用来进行加解密的,LitePal采用的是AES加密算法,而且加密的key可以在LitePal中进行动态设置,不过也支持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进行大小写转换,或者保持不变
- checkConditionsCorrect:检查查询条件是否合理
- isFieldTypeSupported:检查成员变量是否合理
DBUtility
- getTableNameByClassName:通过类名获取表名
- isTableExists:查询表是否存在
SharedUtil
- updateVersion:根据Key升级当前版本号
- getLastVersion:根据Key获取上一个版本
- removeVersion:根据Key移除特定的版本
总结
LitePal利用反射对获取Model的属性,然后拼接成SQL语句,对于调用者来说减少了很多重复性操作,升级也比较方便,符合Java的面向对象,使用者不用再关心底层的SQL语句,当然LitePal同时也提供了使用SQL语句查询的功能,使用者可以凭喜好使用,看了SQL的源码实现之后,个人觉得有些地方可以改进。
反射优化
LitePal的CRUD操作每次都需要反射,这些在数据量少的时候影响还好,在数据量较大的时候会比较影响效率,可以对已经反射过的Model属性缓存起来,那么下一次CRUD操作的时候如果是同一个Model,那么直接从缓存中取,那么效率就会快一些。
同步优化
LitePal的CRUD操作全部都使用了synchronized关键字,其实如果我们的操作是单线程中进行,没必要上锁,可以再提供一个重载的非同步方法,因为很多时候CRUD操作都是在单线程中的。
单例优化
LitePal的每一次CRUD操作,都需要对DataHandler的子类SaveHandler、QueryHandler进行一次实例化,如果采用单例结合Builder模式的话可以减少一部分开销,还有ClusterQuery也是如此。
线程优化
LitePal开启多线程的时候是采用的new Thread方式,如果有多个异步的DB操作的时候,会创建很多个线程,如果使用线程池可以减少一部分开销。
其实上面的一些建议只是我个人的理解,LitePal已经是一款很优秀的框架,希望LitePal这款ORM框架越来越强。