在该篇博客中会通过分析一次ormlite的query操作,来对ormlite的源码进行研究

一、关于Anroid数据库的使用

最基本的数据库操作流程为
(1)首先得到要操作的db
(2)调用insert update delete rawQuery query等方法,得到cursor
(3)操作得到的Cursor对象
(4)调用SQLiteDatabase的close方法释放数据库连接,否则容易出现SQLiteException

一般我们的实现为
(1)定义一个dbHelper继承SQLiteOpenHelper,用来完成数据库升级操作
(2)定义一个DBManager,比如BaseDBProvider,封装我们的业务方法,在DBManager构造函数或者某个方法中实例化BaseDBOpenHelper,并得到一个SQLiteDatabase对象,比如getSQLiteOpenHelper().getWritableDatabase();
(3)在DBManager中定义insert delete update query等操作
(4)调用closeDB方法 在整个应用关闭中执行

在我们调用getSQLiteOpenHelper().getWritableDatabase();时实际上sqliteOpenHelper就会去完成oncreate和onupgrade等方法
在使用com.j256.ormlite.android中我们使用insert等方法,会调用到BaseDaoImpl的create方法,该方法实际上会调用到

1
DatabaseConnection connection = connectionSource.getReadWriteConnection();

此方法会调用到AndroidConnectSource的getReadWriteConnection方法,其中有

1
db = helper.getWritableDatabase();

在该方法中会调用到

1
2
3
public synchronized SQLiteDatabase getWritableDatabase() {
return super.getWritableDatabase(getSecKey());
}

最终调用了父类的方法,这里的父类是net.sqlcipher.database中的SQLiteOpenHelper的getWriteDatabase方法。
在该方法中有一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int version = db.getVersion();
if (version != mNewVersion) {
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
onUpgrade(db, version, mNewVersion);
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}

其中onCreate和onUpgrade就是在这里调用的。sqlite也是类似。

二、一次数据库query分析

同样query等操作也是调用到了BaseDaoImpl的相应方法。
比如我们常用的query方法:queryBuilder().where().eq(fieldName, value).query();
在该query方法中会调用到QueryBuilder的query方法,最终会调用到ormlite的BaseDaoImpl的query方法。

1
2
3
4
5
6
/**
* A short cut to {@link Dao#query(PreparedQuery)}.
*/
public List<T> query() throws SQLException {
return dao.query(prepare());
}

其中调用了ormlite的StatementBuilder的如下方法完成了查询语句的prepare。

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);
}

这里要注意QueryBuilder和StatementBuilder。QueryBuilder是StatementBuilder的子类,主要用于构建select语句。
回到query方法,他会调用到ormlite的BaseDaoImpl中的query方法。

1
2
3
4
public List<T> query(PreparedQuery<T> preparedQuery) throws SQLException {
checkForInitialized();
return statementExecutor.query(connectionSource, preparedQuery, objectCache);
}

该方法会调用到ormlite中的StatementExecutor的query方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Return a list of all of the data in the table that matches the {@link PreparedStmt}. Should be used carefully if
* the table is large. Consider using the {@link Dao#iterator} if this is the case.
*/
public List<T> query(ConnectionSource connectionSource, PreparedStmt<T> preparedStmt, ObjectCache objectCache)
throws SQLException {
SelectIterator<T, ID> iterator =
buildIterator(/* no dao specified because no removes */null, connectionSource, preparedStmt, objectCache,
DatabaseConnection.DEFAULT_RESULT_FLAGS);
try {
List<T> results = new ArrayList<T>();
while (iterator.hasNextThrow()) {
results.add(iterator.nextThrow());
}
logger.debug("query of '{}' returned {} results", preparedStmt.getStatement(), results.size());
return results;
} finally {
iterator.close();
}
}

注意到其中就是调用到了buildIterator方法,完成了数据库的查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public SelectIterator<T, ID> buildIterator(BaseDaoImpl<T, ID> classDao, ConnectionSource connectionSource,
PreparedStmt<T> preparedStmt, ObjectCache objectCache, int resultFlags) throws SQLException {
DatabaseConnection connection = connectionSource.getReadOnlyConnection();
CompiledStatement compiledStatement = null;
try {
compiledStatement = preparedStmt.compile(connection, StatementType.SELECT, resultFlags);
SelectIterator<T, ID> iterator =
new SelectIterator<T, ID>(tableInfo.getDataClass(), classDao, preparedStmt, connectionSource,
connection, compiledStatement, preparedStmt.getStatement(), objectCache);
connection = null;
compiledStatement = null;
return iterator;
} finally {
if (compiledStatement != null) {
compiledStatement.close();
}
if (connection != null) {
connectionSource.releaseConnection(connection);
}
}
}

其中调用了SelectIterator构造函数,该构造函数中会调用到ormlite的AndroidCompiledStatement的runQuery方法。其中AndroidCompileStatement是ormlite-android中的类,他继承ormlite-core中的类CompiledStatement。
该方法会调用到该类中的getCursor方法,该getCursor方法得到数据库相关的游标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Cursor getCursor() throws SQLException {
if (cursor == null) {
String finalSql = null;
try {
if (max == null) {
finalSql = sql;
} else {
finalSql = sql + " " + max;
}
if (cancelQueriesEnabled) {
cancellationHook = apiCompatibility.createCancellationHook();
}
cursor = apiCompatibility.rawQuery(db, finalSql, getStringArray(), cancellationHook);
cursor.moveToFirst();
logger.trace("{}: started rawQuery cursor for: {}", this, finalSql);
} catch (android.database.SQLException e) {
throw SqlExceptionUtil.create("Problems executing Android query: " + finalSql, e);
}
}

return cursor;
}

其中的rawQuery实际上通过类JellyBeanApiCompatibility的rawQuery方法调用了sqlcipher包中的SQLiteDatabase的rawQuery方法。该JellyBeanApiCompatibility类Basic class which provides no-op methods for all Android version.是一个基类,用来给所有Android版本提供非op操作的。

该SQLiteDatabase的rawQuery方法调用到了rawQueryWithFactory方法。该方法关键的两行代码是

1
2
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
cursor = driver.query(cursorFactory != null ? cursorFactory : mFactory,selectionArgs);

调用到了SQLiteDirectCursorDriver的query方法,该方法通过

1
mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);

得到cursor。在该cursor中,mCount代表结果个数。但是发现经过构造函数初始化时,mCount仍然为-1,那么查询到底在哪里进行的呢?
我们注意到在AndroidCompiledStatement的getCursor方法中的rawQuery下面一行是

1
cursor.moveToFirst

该方法Move the cursor to the first row.This method will return false if the cursor is empty.该moveToFirst实际上会调用SQLiteCursor中的getCount方法,该方法会调用fillWindow方法。在该方法中,则是调用到SQLiteQuery的fillWindow方法,并将mCount返回,该fillWindow方法的注释是Reads rows into a buffer. This method acquires the database lock. (这里的lock应该是读锁)。实际上是在这里进行的数据库相关的操作的。

在StatementExecutor的query中,在得到了iterator后,会调用results.add(iterator.nextThrow());完成关系到java对象的映射,其中调用了
BaseMappedQuery的mapRow方法,在该方法中调用了

1
2
3
for (FieldType fieldType : resultsFieldTypes) {
Object val = fieldType.resultToJava(results, colPosMap);
}

其中resultsFieldTypes为相应数据表的实体类,根据我们指定的注解为DatabaseField的字段去数据库中查找该字段。如果找不到,则会抛出SQLException错误。如此完成关系和对象的映射。

参考文献

[1]http://ormlite.com/sqlite_java_android_orm.shtml