1.结构分析

1.1 UML类图

litepal 类关联图

1.2 UML时序图

litepal 初始化时序图

1.3 表的创建与分类

作者在存储一个对象时,将所有字段类型分别转换成如下三种表的形式

  1. 本体基本数据类型和一对多和多对一关联外键表,这个是主体表,表名字是className
  2. 本体基本数据集合类型表(list或者set类型字段),这个是第二张表,表名是className_fieldName
  3. 本体多对多关系的中间表,这个是第三张表,表名是className_associationClassName

其中比较重要的解释就是关联表的处理,下文继续解释

2.增删改查的逻辑实现

2.1 映射关系的处理(重点)

介绍增删改查逻辑,首先需要考虑表关联在orm框架中的Bean关系转换。以下是源码中的注释,比较详细的描述了Bean关系的分类

litepal Bean关系的描述

原文大意如下

处理一对多关系的的情况。比如Song Alum,一个Alum有多首Song,一首Song属于一个Alum。所以如果Song中定义一个私有Alum变量,Alum中有 个关于Song的List或者Set的私有变量,那么Alum与Song就有一对多的关系。如果Alum中没有关于Song的List或者Set,或者只有一个Song私有变 量,那么Alum对于Song就是一对一的关系。处理多对一关系的情况。Song这边只需要关于Alum的FK即可。如果是多对多的关系,就需要中间表来处理, 并且中间表的名字是根据两个表名字的字母顺序来创建的

一图胜千言

关系简图

一个对象与其他对象的关系可以归结为这几种

关联 解释 关系 备注
A -> C1 A中有C1,C1中有A 一对一
A -> C2 A中有C2,C1中有A的集合 多对一
A -> C3 A中有C3,C3中无A 一对一
A -> C4 A中有C4的集合,C4中有A 一对多
A -> C5 A中有C5的集合,C5中有A的集合 多对多 需要中间表
A -> C6 A中有C6的集合,C6中无A 一对多
litepal作者简化为三种关系

一对一 ;多对一(一对多) ; 多对多

代码实现如下,在LitepalBase中可以找到,oneToAnyConditions代码如下,注意代码中的注释的位置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private void oneToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
		Class<?> fieldTypeClass = field.getType();
    if (LitePalAttr.getInstance().getClassNames().contains(fieldTypeClass.getName())) {
        ...
        for (Field reverseField : reverseFields) {
            if (!Modifier.isStatic(reverseField.getModifiers())) {
                Class<?> reverseFieldTypeClass = reverseField.getType();
                if (className.equals(reverseFieldTypeClass.getName())) {
                    //创建一对一的关系   A -> C1 
                    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, reverseField, Const.Model.ONE_TO_ONE);
                    }
                    reverseAssociations = true;
                }
                else if (isCollection(reverseFieldTypeClass)) {
                    String genericTypeName = getGenericTypeName(reverseField);
                    if (className.equals(genericTypeName)) {
                        //创建多对一的关系  A -> C2  区分一对多的关系,外键绑定了自己的类名
                        if (action == GET_ASSOCIATIONS_ACTION) {
                            addIntoAssociationModelCollection(className, fieldTypeClass.getName(),className, Const.Model.MANY_TO_ONE);
                        } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                            addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),className, field, reverseField, Const.Model.MANY_TO_ONE);
                        }
                        reverseAssociations = true;
                    }
                }
            }
        }
        if (!reverseAssociations) {
            //创建一对一的关系 A -> C3
            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 分析代码如下,注意代码中的注释的位置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private void manyToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
	if (isCollection(field.getType())) {
		String genericTypeName = getGenericTypeName(field);
        if (LitePalAttr.getInstance().getClassNames().contains(genericTypeName)) {
            ...
            for (Field reverseField : reverseFields) {
                if (!Modifier.isStatic(reverseField.getModifiers())) {
                    Class<?> reverseFieldTypeClass = reverseField.getType();
                    if (className.equals(reverseFieldTypeClass.getName())) {
                        //创建一对多的关系   A -> C4 区分多对一的关系,外键绑定了关联类的类名
                        if (action == GET_ASSOCIATIONS_ACTION) {
                            addIntoAssociationModelCollection(className, genericTypeName,genericTypeName, Const.Model.MANY_TO_ONE);
                        } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                            addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,field, reverseField, Const.Model.MANY_TO_ONE);
                        }
                        reverseAssociations = true;
                    }
                    else if (isCollection(reverseFieldTypeClass)) {
                        String reverseGenericTypeName = getGenericTypeName(reverseField);
                        if (className.equals(reverseGenericTypeName)) {
                            //创建多对多的关系   A -> C5  需要中间表
                            if (action == GET_ASSOCIATIONS_ACTION) {
                                if (className.equalsIgnoreCase(genericTypeName)) {
                                    GenericModel genericModel = new GenericModel();
                                    genericModel.setTableName(DBUtility.getGenericTableName(className, field.getName()));
                                    genericModel.setValueColumnName(DBUtility.getM2MSelfRefColumnName(field));
                                    genericModel.setValueColumnType("integer");
                                    genericModel.setValueIdColumnName(DBUtility.getGenericValueIdColumnName(className));
                                    mGenericModels.add(genericModel);
                                } else {
                                    addIntoAssociationModelCollection(className, genericTypeName, null,Const.Model.MANY_TO_MANY);
                                }
                            } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                                if (!className.equalsIgnoreCase(genericTypeName)) {
                                    addIntoAssociationInfoCollection(className, genericTypeName, null, field,reverseField, Const.Model.MANY_TO_MANY);
                                }
                            }
                            reverseAssociations = true;
                        }
                    }
                }
            }
            if (!reverseAssociations) {
                //创建一对多的关系 A -> C6
                if (action == GET_ASSOCIATIONS_ACTION) {
                    addIntoAssociationModelCollection(className, genericTypeName,genericTypeName, Const.Model.MANY_TO_ONE);
                } else if (action == GET_ASSOCIATION_INFO_ACTION) {
                    addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,field, null, Const.Model.MANY_TO_ONE);
                }
            }
        } else if(BaseUtility.isGenericTypeSupported(genericTypeName) && action == GET_ASSOCIATIONS_ACTION) {
            //基本数据的集合表 如int String等 这些数据是通过一个单独的表来表示
            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);
        }
    }
}

2.2 增操作的逻辑分析

通过实体继承的LitepalSupport对象提供的save()方法可以触发增操作,在save()方法中会将具体处理细节交给 SaveHandler来处理,以下截图为SaveHandler中onSave()的代码逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void onSave(LitePalSupport baseObj) throws SecurityException, IllegalArgumentException,NoSuchMethodException, IllegalAccessException,InvocationTargetException {
    String className = baseObj.getClassName();
    List<Field> supportedFields = getSupportedFields(className);
    List<Field> supportedGenericFields = getSupportedGenericFields(className);
    Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);
    if (!baseObj.isSaved()) {
        analyzeAssociatedModels(baseObj, associationInfos);
        doSaveAction(baseObj, supportedFields, supportedGenericFields);
        analyzeAssociatedModels(baseObj, associationInfos);
    } else {
        analyzeAssociatedModels(baseObj, associationInfos);
        doUpdateAction(baseObj, supportedFields, supportedGenericFields);
    }
}

通过代码中getSupportedFields,getSupportedGenericFields,getAssociationInfo三个方法会获得当前 实体所支持的基本存储类型和关联类型的信息集合

analyzeAssociatedModels方法会循环遍历getAssociationInfo的关联类,如果关联类存储过就会将当前类赋值 给关联类,并且更新外键

doSaveAction方法会做具体的存储操作,开始会通过beforeSave()方法创建ContentValues来提供数据库存储值。 然后调用saving()来具体存储,最后会调用afterSave()将存储获得id赋值给关联类

save时序图

分析器的工作流程在下文有专门的分析章节,这里我们着重于存储逻辑的分析。首先我们看第一个问题。字段分析得到的 三个集合具体是各自都是什么。

getSupportedFields获得的都是普通基本类型,并且会忽略掉有忽略注解的字段,并且通过递归调用获得父类的支持字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void recursiveSupportedFields(Class<?> clazz, List<Field> supportedFields) {
    if (clazz == LitePalSupport.class || clazz == Object.class) {
        return;
    }
    Field[] fields = clazz.getDeclaredFields();
    if (fields != null && fields.length > 0) {
        for (Field field : fields) {
            Column annotation = field.getAnnotation(Column.class);
            if (annotation != null && annotation.ignore()) {
                continue;
            }
            int modifiers = field.getModifiers();
            if (!Modifier.isStatic(modifiers)) {
                Class<?> fieldTypeClass = field.getType();
                String fieldType = fieldTypeClass.getName();
                if (BaseUtility.isFieldTypeSupported(fieldType)) {
                    supportedFields.add(field);
                }
            }
        }
    }
    //递归调用
    recursiveSupportedFields(clazz.getSuperclass(), supportedFields);
}

BaseUtility的isFieldTypeSupported如下,支持的数据就是基本类型和字符串和时间

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static boolean isFieldTypeSupported(String fieldType) {
	if ("boolean".equals(fieldType) || "java.lang.Boolean".equals(fieldType)) {
		return true;
	}
	if ("float".equals(fieldType) || "java.lang.Float".equals(fieldType)) {
		return true;
	}
	if ("double".equals(fieldType) || "java.lang.Double".equals(fieldType)) {
		return true;
	}
	if ("int".equals(fieldType) || "java.lang.Integer".equals(fieldType)) {
		return true;
	}
	if ("long".equals(fieldType) || "java.lang.Long".equals(fieldType)) {
		return true;
	}
	if ("short".equals(fieldType) || "java.lang.Short".equals(fieldType)) {
		return true;
	}
	if ("char".equals(fieldType) || "java.lang.Character".equals(fieldType)) {
		return true;
	}
	if ("[B".equals(fieldType) || "[Ljava.lang.Byte;".equals(fieldType)) {
		return true;
	}
	if ("java.lang.String".equals(fieldType) || "java.util.Date".equals(fieldType)) {
		return true;
	}
	return false;
}

getSupportedGenericFields主要是分析List和Set字段数据,同时也是递归获取父类的字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void recursiveSupportedGenericFields(Class<?> clazz, List<Field> supportedGenericFields) {
    if (clazz == LitePalSupport.class || clazz == Object.class) {
        return;
    }
    Field[] fields = clazz.getDeclaredFields();
    if (fields != null && fields.length > 0) {
        for (Field field : fields) {
            Column annotation = field.getAnnotation(Column.class);
            if (annotation != null && annotation.ignore()) {
                continue;
            }
            int modifiers = field.getModifiers();
            if (!Modifier.isStatic(modifiers) && isCollection(field.getType())) {
                String genericTypeName = getGenericTypeName(field);
                //基本类型和当前对象
                if (BaseUtility.isGenericTypeSupported(genericTypeName) || clazz.getName().equalsIgnoreCase(genericTypeName)) {
                    supportedGenericFields.add(field);
                }
            }
        }
    }
    //递归
    recursiveSupportedGenericFields(clazz.getSuperclass(), supportedGenericFields);
}

getAssociationInfo中重点分析该类的表关联关系,一对其他和多对其他的关系,本小节开头介绍的一个对象关联 关系就在oneToAnyConditions和manyToAnyConditions中去实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
private void analyzeClassFields(String className, int action) {
    try {
        Class<?> dynamicClass = Class.forName(className);
        Field[] fields = dynamicClass.getDeclaredFields();
        for (Field field : fields) {
            if (isNonPrimitive(field)) {
                Column annotation = field.getAnnotation(Column.class);
                if (annotation != null && annotation.ignore()) {
                    continue;
                }
                //关联关系分析
                oneToAnyConditions(className, field, action);
                manyToAnyConditions(className, field, action);
            }
        }
    } catch (ClassNotFoundException ex) {
        ex.printStackTrace();
        throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);
    }
}

经过这三个大步骤,最后将一个对象的如下字段都分析了一遍

类型
1.基本数据类型,字符串和时间类型
2.基本数据类型的List和Set的集合数据
3.其他Bean所产生的一对其他的关联关系数据
4.其他Bean的List或者Set集合产生的多对其他的关联关系数据

onSave中会先执行beforeSave方法

1
2
3
4
5
6
7
8
private void beforeSave(LitePalSupport baseObj, List<Field> supportedFields, ContentValues values)
			throws SecurityException, IllegalArgumentException, NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
    //将supportedFields的基本数据添加到ContentValues中,除开_id或者id的字段
    putFieldsValue(baseObj, supportedFields, values);
    //将分析器的分析阶段所得出的外键集合统一变成tablename_id添加到ContentValues中
    putForeignKeyValue(values, baseObj);
}

然后调用saving方法,一目了然,调用了SQLite的insert操作,同时将存储后得到的id返回

1
2
3
4
5
6
private long saving(LitePalSupport baseObj, ContentValues values) {
    if (values.size() == 0) {
        values.putNull("id");
    }
    return mDatabase.insert(baseObj.getTableName(), null, values);
}

最后调用afterSave,处理其他事务

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private void afterSave(LitePalSupport baseObj, List<Field> supportedFields,List<Field> supportedGenericFields, long id) throws IllegalAccessException, InvocationTargetException {
    //判断id是否合法
    throwIfSaveFailed(id);
    //将id赋值给baseObjId,同时也赋值给名字是_id或者id的字段
    assignIdValue(baseObj, getIdField(supportedFields), id);
    //更新generic数据,通过表名为baseObjectName_fieldName来更新数据
    updateGenericTables(baseObj, supportedGenericFields, id);
    //会将关联类的表中当前对象的id更新成当前存储的所得的id
    updateAssociatedTableWithFK(baseObj);
    //将中间表的双方的id都更新
    insertIntermediateJoinTableValue(baseObj, false);
}

2.3 删操作的逻辑分析

有了对增操作的逻辑的理解,对于删除操作的逻辑基本也能猜到个大概了。基本流程如下

delete时序图

基本业务实现还是在DeleteHandler,针对每个操作,Litepal都有相应的命名的Handler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int onDelete(LitePalSupport baseObj) {
    if (baseObj.isSaved()) {
        //1.获取基本类型的集合的数据集
        List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());
        //2.删除基本类型集合的数据库记录
        deleteGenericData(baseObj.getClass(), supportedGenericFields, baseObj.getBaseObjId());
        //3.分析得到关联类的关系集合
        Collection<AssociationsInfo> associationInfos = analyzeAssociations(baseObj);
        //4.删除外键记录和中间表记录
        int rowsAffected = deleteCascade(baseObj);
        //5.删除自身
        rowsAffected += mDatabase.delete(baseObj.getTableName(), "id = "+ baseObj.getBaseObjId(), null);
        //6.将自身的关联的类对象的存储id都清除
        clearAssociatedModelSaveState(baseObj, associationInfos);
        return rowsAffected;
    }
    return 0;
}

2.4 改操作的逻辑分析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int onUpdate(LitePalSupport baseObj, long id) throws SecurityException, IllegalArgumentException,NoSuchMethodException,IllegalAccessException, InvocationTargetException {
    List<Field> supportedFields = getSupportedFields(baseObj.getClassName());
    List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());
    //1.用将baseObj中的基本集合数据来更新主键为id的baseObj表数据的记录
    updateGenericTables(baseObj, supportedGenericFields, id);
    ContentValues values = new ContentValues();
    //2.将baseObj的数据转换成ContentValues
    putFieldsValue(baseObj, supportedFields, values);
    putFieldsToDefaultValue(baseObj, values, id);
    if (values.size() > 0) {
        //3.使用上边准备的ContentValues更新操作
        return mDatabase.update(baseObj.getTableName(), values, "id = " + id, null);
    }
    return 0;
}

更新操作比较特殊的地方,他不支持直接更新关联表的数据,关联表的数据需要使用当前关联对象自己的update方法来完成更新

2.5 查操作的逻辑分析

查询操作可以完全看作是增操作的逆操作了。

query时序图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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));
            String tableName = getTableName(modelClass);
            //查找符合条件的数据库记录
            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 {
                    //反射创建对象
                    T modelInstance = (T) createInstanceFromClass(modelClass);
                    //填补对象id
                    giveBaseObjIdValue((LitePalSupport) modelInstance,cursor.getLong(cursor.getColumnIndexOrThrow("id")));
                    //填补对象的基本类型数据
                    setValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);
                    //填补对象的基本类型集合数据
                    setGenericValueToModel((LitePalSupport) modelInstance, supportedGenericFields, genericModelMap);
                    if (foreignKeyAssociations != null) {
                        //填补关联表对象
                        setAssociatedModel((LitePalSupport) modelInstance);
                    }
                    dataList.add(modelInstance);
                } while (cursor.moveToNext());
                queryInfoCacheSparseArray.clear();
                genericModelMap.clear();
            }
            return dataList;
        } catch (Exception e) {
            throw new LitePalSupportException(e.getMessage(), e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

这里在分析过程发现一个比较迷惑的地方,填补基本数据类型时,后边有段代码是将关联表的对象也填补上,这个地方感觉比较冗余 因为后续会对关联表数据填充做专项处理,目前不太明白作者的意图

4.分析器

在上边几个操作中都用到了分析器,分析器的作用主要是将当前对象所关联的表的信息更新到当前对象,主要涉及了外键的更新 这些外键的信息在每个对象之后的存储更新等操作提供了更加准确的依据,让数据库的信息更加准确。

4.1 作用分析

直接进入代码分析,如下图的判断中,按照作者在之前表关联的关系注释说明中,这个判断应该不是表示baseObj到底是一对多还是 多对一,而不是单纯的多对一(看类的命名的话容易误解),因为作者将一对多和多对一化作同一种类型了,用getClassHoldsForeignKey 返回的值来做区分,如果当前对象baseObj的类名跟getClassHoldsForeignKey获得一样的话,说明是baseObj是多对一的关系 反之就是一对多

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void analyze(LitePalSupport baseObj, AssociationsInfo associationInfo) throws SecurityException,
			IllegalArgumentException, NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
    if (baseObj.getClassName().equals(associationInfo.getClassHoldsForeignKey())) {
        //多对一
        analyzeManySide(baseObj, associationInfo);
    } else {
        //一对多
        analyzeOneSide(baseObj, associationInfo);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private void analyzeManySide(LitePalSupport baseObj, AssociationsInfo associationInfo)throws SecurityException, IllegalArgumentException, NoSuchMethodException,IllegalAccessException, InvocationTargetException {
    LitePalSupport associatedModel = getAssociatedModel(baseObj, associationInfo);
    if (associatedModel != null) {
        //获得关联类对象中的baseObj类型的集合
        Collection<LitePalSupport> tempCollection = getReverseAssociatedModels(associatedModel,associationInfo);
        //检查集合对象是否属于List或者Set类型并且还原为实际类型
        Collection<LitePalSupport> reverseAssociatedModels = checkAssociatedModelCollection(tempCollection, associationInfo.getAssociateSelfFromOtherModel());
        //将还原后的集合类型重新设置给关联类
        setReverseAssociatedModels(associatedModel, associationInfo, reverseAssociatedModels);
        //检查baseObj是否在集合数据中,没有就添加上;并且判断关联类对象是否已经存在于数据库,存在就给baseObj更新外键
        dealAssociatedModelOnManySide(reverseAssociatedModels, baseObj, associatedModel);
    } else {
        //如果关联类对象是空的,就需要将该对象信息添加到外键清理的集合中,用于之后的数据清理工作
        mightClearFKValue(baseObj, associationInfo);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private void analyzeOneSide(LitePalSupport baseObj, AssociationsInfo associationInfo)throws SecurityException, IllegalArgumentException, NoSuchMethodException,IllegalAccessException, InvocationTargetException {
    //获得关联类的集合
    Collection<LitePalSupport> associatedModels = getAssociatedModels(baseObj, associationInfo);
    if (associatedModels == null || associatedModels.isEmpty()) {
        //如果集合为空,表示外键需要得到清理,先将其添加到相关集合,用作后续清理使用
        String tableName = DBUtility.getTableNameByClassName(associationInfo.getAssociatedClassName());
        baseObj.addAssociatedTableNameToClearFK(tableName);
        return;
    }
    for (LitePalSupport associatedModel : associatedModels) {
        //将关联类集合中的每条数据中的baseObj类型的字段都进行赋值
        buildBidirectionalAssociations(baseObj, associatedModel, associationInfo);
        //处理关联关系,根据是否存储过来处理外键的存储位置
        dealAssociatedModelOnOneSide(baseObj, associatedModel);
    }
}

看到这里,其他几步都还好理解,对对象之间的赋值是为了巩固关联关系,而这几个外键记录操作可能分析起来不是很明朗。 我们先看LitepalSupport中对于这几个关于外键的集合的解释

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*** A map contains all the associated models' id with M2O or O2O
* associations. Each corresponding table of these models contains a foreign
* key column.*/
private Map<String, Set<Long>> associatedModelsMapWithFK;

/*** A map contains all the associated models' id with M2O or O2O association.
* Each corresponding table of these models doesn't contain foreign key
* column. Instead self model has a foreign key column in the corresponding
* table.*/
private Map<String, Long> associatedModelsMapWithoutFK;

/*** A map contains all the associated models' id with M2M association.*/
Map<String, List<Long>> associatedModelsMapForJoinTable;

/*** When updating a model and the associations breaks between current model
* and others, if current model holds a foreign key, it need to be cleared.
* This list holds all the foreign key names that need to clear.*/
private List<String> listToClearSelfFK;

/*** When updating a model and the associations breaks between current model
* and others, clear all the associated models' foreign key value if it
* exists. This list holds all the associated table names that need to
* clear.*/
private List<String> listToClearAssociatedFK;

1.associatedModelsMapWithFK 从注释上来看,应该是表示双向关联时的存储关系 存储完当前对象之后还需要将当前对象的存储id更新到关联对象的表记录中

2.associatedModelsMapWithoutFK 从注释上来看,应该表示单向关联时的存储关系 存储玩当前对象之后不需要管关联对象的存储记录的外键更新

3.associatedModelsMapForJoinTable 中间表记录,这是双向的

4.listToClearSelfFK 表示当前对象需要清除的外键集合,用于关联类对象被清除或者不存在时清理 本对象的外键使用

5.listToClearAssociatedFK 表示当前对象与关联对象关系破除时,关联类对象对于当前类外键需要清理的集合

由此可见分析器的作用就是负责更新关联类的关系,对象在存储等操作前后都会导致关联关系的外键的更新, 这块集中记录,在后续集中处理