在该篇博客中会分析ormlite的一些主要文件和一次update操作,来对ormlite的源码进行研究。

一、ormlite主要类浅析

首先对于android端,ormlite由core和android两个部分组成。
首先,我们来看ormlite-core,其中包含core、dao、db、field、logger、misc、stmt、support、table文件夹。其中主要文件夹包含dao、stmt、table、support、field。
1.首先是dao文件夹,包含主要类BaseDaoImpl、Dao、DaoManager、RuntimeExceptionDao,其中BaseDaoImpl是Dao的抽象实现,Dao为接口,RuntimeExceptionDao为Dao的封装,对抛出的错误做了封装。
2.stmt文件夹
(1)该文件夹下包含子文件夹mapped,该文件夹主要进行映射相关的功能,也就是说,将java的变量解析成sql可以理解的变量,例如下面的代码
convertJavaFieldToSqlArgValue(extractJavaFieldValue(object));
(2)接口PreparedStmt,又有三个接口继承该接口,分别为PreparedDelete、PreparedQuery、PreparedUpdate。然后我们看MappedPreparedStmt,该类主要完成预编译,使用预编译对象可以提高效率,提高安全性,提高效率是因为将格式固定的SQL编译后放在内存中,提高安全性是因为防止sql注入[1]
(3)StatementBuilder,顾名思义为构建sql语句的类,这个builder表明他们用了构造者模式,类似PreparedStmt,同样包含三个子类,QueryBuilder、UpdateBuilder、DeleteBuiler,分别对查找、更新和删除进行构建。
比如一次query

1
2
3
4
5
6
7
8
9
QueryBuilder<TestEO, Long> queryBuilder = DBManager.getTestRuntimeDao().queryBuilder();
Where<TestEO, Long> where = queryBuilder.where();
try {
where.eq("deleteTime", 0).and().eq("chargeVisible", 1).and().eq("parentId", 0);
queryBuilder.orderBy("orderNum", true);
return DBManager.getTestRuntimeDao().query(where.prepare());
} catch (SQLException e) {
return null;
}

或者一次update

1
2
3
ub.where().le(“modifyTime", lastReadDbTime).and().le(“createTime", lastReadDbTime);
ub.updateColumnValue(“test", trueCode);
ub.update();

(4)StatementExecutor
类似StatementBuilder为sql语句的构建,那么StatementExecutor主要完成sql语句的执行

二、一次update操作

getDao().update(data);
也就是RuntimeExceptionDao.update

1
2
3
4
5
6
7
8
9
10
11
/**
* @see Dao#update(Object)
*/

public int update(T data) {
try {
return dao.update(data);
} catch (SQLException e) {
logMessage(e, "update threw exception on: " + data);
throw new RuntimeException(e);
}
}

调用到的是BaseDaoImpl的update方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int update(T data) throws SQLException {
checkForInitialized();
// ignore updating a null object
if (data == null) {
return 0;
} else {
DatabaseConnection connection = connectionSource.getReadWriteConnection();
try {
return statementExecutor.update(connection, data, objectCache);
} finally {
connectionSource.releaseConnection(connection);
}
}
}

其中的update也是调用的StatementExecutor的update方法。

1
2
3
4
5
6
7
8
9
/**
* Update an object in the database.
*/

public int update(DatabaseConnection databaseConnection, T data, ObjectCache objectCache) throws SQLException {
if (mappedUpdate == null) {
mappedUpdate = MappedUpdate.build(databaseType, tableInfo);
}
return mappedUpdate.update(databaseConnection, data, objectCache);
}
经过一系列的操作,这里调用到了MappedUpdate的update方法。
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
/**
* Update the object in the database.
*/

public int update(DatabaseConnection databaseConnection, T data, ObjectCache objectCache) throws SQLException {
try {
// there is always and id field as an argument so just return 0 lines updated
if (argFieldTypes.length <= 1) {
return 0;
}
Object[] args = getFieldObjects(data);
Object newVersion = null;
if (versionFieldType != null) {
newVersion = versionFieldType.extractJavaFieldValue(data);
newVersion = versionFieldType.moveToNextValue(newVersion);
args[versionFieldTypeIndex] = versionFieldType.convertJavaFieldToSqlArgValue(newVersion);
}
int rowC = databaseConnection.update(statement, args, argFieldTypes);
if (rowC > 0) {
if (newVersion != null) {
// if we have updated a row then update the version field in our object to the new value
versionFieldType.assignField(data, newVersion, false, null);
}
if (objectCache != null) {
// if we've changed something then see if we need to update our cache
Object id = idField.extractJavaFieldValue(data);
T cachedData = objectCache.get(clazz, id);
if (cachedData != null && cachedData != data) {
// copy each field from the updated data into the cached object
for (FieldType fieldType : tableInfo.getFieldTypes()) {
if (fieldType != idField) {
fieldType.assignField(cachedData, fieldType.extractJavaFieldValue(data), false,
objectCache);
}
}
}
}
}
logger.debug("update data with statement '{}' and {} args, changed {} rows", statement, args.length, rowC);
if (args.length > 0) {
// need to do the (Object) cast to force args to be a single object
logger.trace("update arguments: {}", (Object) args);
}
return rowC;
} catch (SQLException e) {
throw SqlExceptionUtil.create("Unable to run update stmt on object " + data + ": " + statement, e);
}
}

首先看其中的getFieldObjects方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Return the array of field objects pulled from the data object.
*/

protected Object[] getFieldObjects(Object data) throws SQLException {
Object[] objects = new Object[argFieldTypes.length];
for (int i = 0; i < argFieldTypes.length; i++) {
FieldType fieldType = argFieldTypes[i];
if (fieldType.isAllowGeneratedIdInsert()) {
objects[i] = fieldType.getFieldValueIfNotDefault(data);
} else {
objects[i] = fieldType.extractJavaFieldToSqlArgValue(data);
}
if (objects[i] == null && fieldType.getDefaultValue() != null) {
objects[i] = fieldType.getDefaultValue();
}
}
return objects;
}

注意这里的argFieldTypes,在update中有build方法

1
2
3
if (mappedUpdate == null) {
mappedUpdate = MappedUpdate.build(databaseType, tableInfo);
}

在该build中完成的argFieldTypes的构造,同时会调用到MappedUpdate的构造函数,在父类的构造函数BaseMappedStatement完成argFieldTypes赋值。
该初始化函数也可以在query或者update时调用queryBuilder或者UpdateBuiler的prepare完成的,其中prepare会调用到statementBuilder的prepareStatement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected MappedPreparedStmt<T, ID> prepareStatement(Long limit) throws SQLException {
List<ArgumentHolder> argList = new ArrayList<ArgumentHolder>();
String statement = buildStatementString(argList);
ArgumentHolder[] selectArgs = argList.toArray(new ArgumentHolder[argList.size()]);
FieldType[] resultFieldTypes = getResultFieldTypes();
FieldType[] argFieldTypes = new FieldType[argList.size()];;
for (int selectC = 0; selectC < selectArgs.length; selectC++) {
argFieldTypes[selectC] = selectArgs[selectC].getFieldType();
}
if (!type.isOkForStatementBuilder()) {
throw new IllegalStateException("Building a statement from a " + type + " statement is not allowed");
}
return new MappedPreparedStmt<T, ID>(tableInfo, statement, argFieldTypes, resultFieldTypes, selectArgs,
(databaseType.isLimitSqlSupported() ? null : limit), type);
}

其中就完成了MappedPreparedStmt的构造,而MappedPreparedStmt是BaseMappedStatement的子类。
注意BaseMappedStatement中的getFieldObjects中的这一段代码

1
objects[i] = fieldType.extractJavaFieldToSqlArgValue(data);

其中调用了FieldType的convertJavaFieldToSqlArgValue(extractJavaFieldValue(object));方法

1
2
3
4
5
6
7
8
9
10
11
public Object extractJavaFieldValue(Object object) throws SQLException {

Object val = extractRawJavaFieldValue(object);

// if this is a foreign object then we want its id field
if (foreignIdField != null && val != null) {
val = foreignIdField.extractRawJavaFieldValue(val);
}

return val;
}

最终调用到了FieldType的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Return the value from the field in the object that is defined by this FieldType.
*/

public <FV> FV extractRawJavaFieldValue(Object object) throws SQLException {
Object val;
if (fieldGetMethod == null) {
try {
// field object may not be a T yet
val = field.get(object);
} catch (Exception e) {
throw SqlExceptionUtil.create("Could not get field value for " + this, e);
}
} else {
try {
val = fieldGetMethod.invoke(object);
} catch (Exception e) {
throw SqlExceptionUtil.create("Could not call " + fieldGetMethod + " for " + this, e);
}
}

@SuppressWarnings("unchecked")
FV converted = (FV) val;
return converted;
}

其中的object为data数据对象,

随后会调用FieldType的convertJavaFieldToSqlArgValue方法

1
2
3
4
5
6
7
8
9
10
/**
* Convert a field value to something suitable to be stored in the database.
*/

public Object convertJavaFieldToSqlArgValue(Object fieldVal) throws SQLException {
if (fieldVal == null) {
return null;
} else {
return fieldConverter.javaToSqlArg(this, fieldVal);
}
}

随后来看代码

1
int rowC = databaseConnection.update(statement, args, argFieldTypes);

其中会调用到我设置的ConnectionProxy的update方法

1
2
3
4
5
6
7
8
9
@Override
public int update(String statement, Object[] args, FieldType[] argfieldTypes) throws SQLException {
int ret = super.update(statement, args, argfieldTypes);
Matcher matcher = updatePattern.matcher(statement);
if (!getDbHelper().getWritableDatabase().inTransaction() && matcher.find()) {
postChangeEvent(matcher.group(1));
}
return ret;
}

随后进行 调用AndroidDatabaseConnection的update方法

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
private int update(String statement, Object[] args, FieldType[] argFieldTypes, String label) throws SQLException {
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement(statement);
bindArgs(stmt, args, argFieldTypes);
stmt.execute();
} catch (android.database.SQLException e) {
throw SqlExceptionUtil.create("updating database failed: " + statement, e);
} finally {
if (stmt != null) {
stmt.close();
stmt = null;
}
}
int result;
try {
stmt = db.compileStatement("SELECT CHANGES()");
result = (int) stmt.simpleQueryForLong();
} catch (android.database.SQLException e) {
// ignore the exception and just return 1
result = 1;
} finally {
if (stmt != null) {
stmt.close();
}
}
logger.trace("{} statement is compiled and executed, changed {}: {}", label, result, statement);
return result;
}

其中调用了stmt.execute();也就是sqlcipher中SQLiteStatement的execute方法。

参考文献
[1]http://blog.csdn.net/theorytree/article/details/7331096