在该篇博客中会通过分析一次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 { * / 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 */ 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下面一行是
该方法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