GreenDAO基本使用及源碼分析
GreenDAO介紹
GreenDAO is a light & fast ORM for Android that maps objects to SQLite databases. Being highly optimized for Android, greenDAO offers great performance and consumes minimal memory.
GreenDAO是爲Android系統開發的一個輕量級且快速的ORM(對象/關係映射),它可以將對象映射到SQLite數據庫。GreenDAO針對Android系統做了高度優化,提供了出色的性能,並且佔用非常少內存。
GreenDAO通過ORM,使開發者可以使用簡單的面向對象API來進行數據庫操作,提高了開發效率。
GreenDAO基本使用
添加依賴
在根build.gradle
中:
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin
}
}
在module的build.gradle
中:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
dependencies {
implementation 'org.greenrobot:greendao:3.2.2' // add library
}
創建存儲對象實體類
@Entity
public class Student {
int studentId;
int age;
}
Gradle同步之後,會指定生成該實體類的set/get
函數
@Entity
public class Student {
int studentId;
int age;
String name;
@Generated(hash = 574930005)
public Student(int studentId, int age, String name) {
this.studentId = studentId;
this.age = age;
this.name = name;
}
@Generated(hash = 1556870573)
public Student() {
}
public int getStudentId() {
return this.studentId;
}
public void setStudentId(int studentId) {
this.studentId = studentId;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
同時,會自動生成下面幾個類:
下面是這幾個類的作用:
- DaoMaster:GreenDao的總入口,負責整個庫的運行,其內部類OpenHelper和DevOpenHelper是SQLiteOpenHelper實現
- DaoSession:會話層,操作Dao的具體對象,包括DAO對象的註冊
- xxDao:根據每個實體生成的DAO對象,進行具體的數據庫操作
- xxEntity:實體類,和表內容一一對應
下面的UML圖可以更清晰理清這幾個類的關係:
GreenDAO初始化
可以在Application中進行初始化操作,以便在整個APP中維持一個全局的DaoSession
:
@Override
public void onCreate() {
super.onCreate();
initGreenDao();
}
private void initGreenDao() {
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "myGreenDAO.db");
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
}
public static DaoSession getDaoSession() {
return daoSession;
}
GreenDAO實現數據庫增刪改查
增
private void insertDB() {
Student student2 = new Student();
student2.setAge(13);
student2.setName("小花");
student2.setStudentId(3002);
GreenDAOApplication.getDaoSession().insert(student2);
Student student1 = new Student();
student1.setAge(14);
student1.setName("小明");
student1.setStudentId(3001);
GreenDAOApplication.getDaoSession().insert(student1);
}
運行之後數據庫創建在 data/data/com.saberhao.greendaodemo/databases
目錄下:
打開數據庫,可以看到我們插入的數據:
刪
private void deleteFromDB(Student s) {
GreenDAOApplication.getDaoSession().delete(s);
}
改
private void updateDB(Student s) {
GreenDAOApplication.getDaoSession().update(s);
}
查
public List<Student> queryData(String s) {
List<Student> students = GreenDAOApplication.getDaoSession().queryRaw(Student.class, " where Id = ?", s);
return students;
}
GreenDao源碼分析
下面源碼均來自GreenDao最新的3.2.2版本
1. 創建數據庫幫助類對象DevOpenHelper
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "myGreenDAO.db");
DevOpenHelper
繼承自OpenHelper
,OpenHelper
繼承自DatabaseOpenHelper
,DatabaseOpenHelper
繼承自原生SQLiteOpenHelper
。
public static class DevOpenHelper extends OpenHelper {
...
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
}
public static abstract class OpenHelper extends DatabaseOpenHelper {
...
@Override
public void onCreate(Database db) {
Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
createAllTables(db, false);
}
}
可以看到DevOpenHelper
upgrade的時候會丟棄所有表,並新建所有表
2. 獲取數據庫
SQLiteDatabase db = helper.getWritableDatabase();
這裏獲取的是SQLiteDatabase
類型的數據庫
public SQLiteDatabase getWritableDatabase() {
synchronized (this) {
return getDatabaseLocked(true);
}
}
DatabaseOpenHelper
還爲我們提供了兩種類型的數據庫:標準型的數據庫StandardDatabase
,另一種是加密型的數據庫SQLCipher EncryptedDatabase
public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {
public Database getWritableDb() {
return wrap(getWritableDatabase());
}
public Database getEncryptedWritableDb(String password) {
EncryptedHelper encryptedHelper = checkEncryptedHelper();
return encryptedHelper.wrap(encryptedHelper.getWritableDatabase(password));
}
}
StandardDatabase
和EncryptedDatabase
均實現了 Database接口,我們只要在使用前指定數據庫類型,之後的數據庫操作都是以代理形式進行,使用的接口完全一致。
public interface Database {
Cursor rawQuery(String sql, String[] selectionArgs);
void execSQL(String sql) throws SQLException;
void beginTransaction();
void endTransaction();
boolean inTransaction();
void setTransactionSuccessful();
void execSQL(String sql, Object[] bindArgs) throws SQLException;
DatabaseStatement compileStatement(String sql);
boolean isDbLockedByCurrentThread();
void close();
Object getRawDatabase();
}
3. 創建DaoMaster對象
DaoMaster daoMaster = new DaoMaster(db);
將原生數據庫對象SQLiteDatabase
傳入,獲取DaoMaster
對象
public class DaoMaster extends AbstractDaoMaster {
public DaoMaster(SQLiteDatabase db) {
this(new StandardDatabase(db));
}
public DaoMaster(Database db) {
//-->2.1 傳入schema版本和db到父類的構造函數中
super(db, SCHEMA_VERSION);
//-->2.2 註冊Dao類型
registerDaoClass(StudentDao.class);
}
}
public abstract class AbstractDaoMaster {
...
//2.1 父類構造函數
public AbstractDaoMaster(Database db, int schemaVersion) {
this.db = db;
this.schemaVersion = schemaVersion;
daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
}
//2.2 註冊Dao類型
protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
DaoConfig daoConfig = new DaoConfig(db, daoClass);
daoConfigMap.put(daoClass, daoConfig);
}
...
}
步驟2.1,在AbstractDaoMaster
對象的構造方法中,主要做了2步操作:
- 記錄當前的數據庫對象和版本
- 創建
daoConfigMap
的HashMap
對象,用於保存DAO對象及其數據配置對象DaoConfig
而後步驟2.2中,調用父類registerDaoClass
函數,創建DaoConfig
對象,並將其和DAO對象於daoConfigMap
中綁定。
4. AbstractDao構造函數
public AbstractDao(DaoConfig config, AbstractDaoSession daoSession) {
this.config = config;
this.session = daoSession;
db = config.db;
isStandardSQLite = db.getRawDatabase() instanceof SQLiteDatabase;
identityScope = (IdentityScope<K, T>) config.getIdentityScope();
if (identityScope instanceof IdentityScopeLong) {
identityScopeLong = (IdentityScopeLong<T>) identityScope;
} else {
identityScopeLong = null;
}
statements = config.statements;
pkOrdinal = config.pkProperty != null ? config.pkProperty.ordinal : -1;
}
在繼續其他源碼分析前,我們需要在看看AbstractDao
構造函數,這個函數會對我們後面用到的幾個參數進行初始化,包括:isStandardSQLite
,identityScope
,identityScopeLong
,statements
,pkOrdinal
,後面用到的時候大家就知道在這裏初始化的。
5. 創建DaoSeesion對象
daoSession = daoMaster.newSession();
在DaoMaster中進行初始化
//@DaoMaster.java
public DaoSession newSession() {
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
//@DaoSession.java
public class DaoSession extends AbstractDaoSession {
public DaoSession(...) {
//-->5.1調用父類構造函數
super(db);
//獲取DaoConfig對象
studentDaoConfig = daoConfigMap.get(StudentDao.class).clone();
//-->5.2 初始化identityScope
studentDaoConfig.initIdentityScope(type);
//創建ClassDao,將DaoConfig和DaoSeesion傳入ClassDao中
studentDao = new StudentDao(studentDaoConfig, this);
//-->5.3調用父類registerDao方法
registerDao(Student.class, studentDao);
}
}
//@AbstractDaoSession.java
public class AbstractDaoSession {
//5.1調用父類構造函數
public AbstractDaoSession(Database db) {
this.db = db;
//創建entityToDao用於保存Class和ClassDao對於關係
this.entityToDao = new HashMap<Class<?>, AbstractDao<?, ?>>();
}
//5.2調用AbstractDaoSession的registerDao方法
protected <T> void registerDao(Class<T> entityClass, AbstractDao<T, ?> dao) {
//綁定Class和ClassDao
entityToDao.put(entityClass, dao);
}
}
//5.2 初始化[email protected]
public void initIdentityScope(IdentityScopeType type) {
if (type == IdentityScopeType.None) {
identityScope = null;
} else if (type == IdentityScopeType.Session) {
if (keyIsNumeric) {
identityScope = new IdentityScopeLong();
} else {
identityScope = new IdentityScopeObject();
}
} else {
throw new IllegalArgumentException("Unsupported type: " + type);
}
}
從上面的源碼我們可以看到daoMaster.newSession()
主要完成的工作包括:
-
創建了Class實體與Dao對象的映射集合
-
從
daoConfigMap
中獲取Config對象 -
根據
IdentityScopeType
的類型初始化IdentityScope
對象,根據type的不同,它有兩種類型,分別是IdentityScopeObject
和IdentityScopeLong
,它的作用是根據主鍵緩存對應的實體數據。當主鍵是數字類型的時候,如long/Long
、int/Integer
、short/Short
、byte/Byte
,使用IdentityScopeLong
緩存實體數據,當主鍵不是數字類型的時候,則使用IdentityScopeObject
緩存實體數據 -
創建ClassDao,本例指的是studentDao,並將ClassDao和Class建立映射關係,存入entityToDao中
6. 插入
private void insertToDB(Student s) {
GreenDAOApplication.getDaoSession().insert(s);
}
接下來我們繼續看insert是如何工作的
//@AbstractDaoSession。java
public <T> long insert(T entity) {
@SuppressWarnings("unchecked")
//-->6.1獲取ClasseDao實例
AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
//-->6.2通過classDao插入對象
return dao.insert(entity);
}
//6.1獲取ClasseDao實例 @AbstractDaoSession.java
public AbstractDao<?, ?> getDao(Class<? extends Object> entityClass) {
//在entityToDao中,通過class獲取Dao對象
AbstractDao<?, ?> dao = entityToDao.get(entityClass);
...
return dao;
}
//6.2通過classDao出入對象 @AbstractDao.java
public long insert(T entity) {
//-->6.3
return executeInsert(entity, statements.getInsertStatement(), true);
}
statements
是一個TableStatements
對象,在AbstractDao
構造函數中初始化,是一個根據指定的表格創建SQL語句的一個幫助類。statements.getInsertStatement()
是獲得一個插入語句
//6.3.0 @TableStatements.java
public DatabaseStatement getInsertStatement() {
if (insertStatement == null) {
//創建插入sql語句
String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns);
//調用Database接口,StandardDatabase和EncryptedDatabase有不同給實現
//實現將sql語句編譯成當前數據庫(標準或者加密數據庫)對應的語句,
DatabaseStatement newInsertStatement = db.compileStatement(sql);
synchronized (this) {
if (insertStatement == null) {
insertStatement = newInsertStatement;
}
}
if (insertStatement != newInsertStatement) {
newInsertStatement.close();
}
}
return insertStatement;
}
在getInsertStatement
方法中,主要做了兩件事:
- 使用
SqlUtils
創建了插入的sql
語句。 - 根據不同的數據庫類型(標準數據庫或加密數據庫)將
sql
語句編譯成當前數據庫對應的語句。
//6.3.1 @AbstractDao.java
private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
long rowId;
//判斷數據庫是否被當前線程鎖定
if (db.isDbLockedByCurrentThread()) {
//-->6.4直接插入數據
rowId = insertInsideTx(entity, stmt);
} else {
// Do TX to acquire a connection before locking the stmt to avoid deadlocks
db.beginTransaction();
try {
rowId = insertInsideTx(entity, stmt);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
if (setKeyAndAttach) {
updateKeyAfterInsertAndAttach(entity, rowId, true);
}
return rowId;
}
//5.4直接插入數據
private long insertInsideTx(T entity, DatabaseStatement stmt) {
synchronized (stmt) {
if (isStandardSQLite) {
SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
//-->6.5 將數據綁定到Statement中
bindValues(rawStmt, entity);
//-->6.6 執行插入操作
return rawStmt.executeInsert();
} else {
bindValues(stmt, entity);
return stmt.executeInsert();
}
}
}
在6.4中,函數insertInsideTx
使用同步鎖保證線程安全,如果當前是標準數據庫,使用原生SQLiteStatement
進行實體字段屬性的綁定和執行最後的插入操作,如果是加密數據,則直接使用當前的加密數據庫進行操作。其中bindValues
這個方法對應的實現類在StudentDao
中:
//6.5 將數據綁定到Statement中 @StudentDao.java
public class StudentDao extends AbstractDao<Student, Long> {
@Override
protected final void bindValues(DatabaseStatement stmt, Student entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
stmt.bindLong(2, entity.getStudentId());
stmt.bindLong(3, entity.getAge());
String name = entity.getName();
if (name != null) {
stmt.bindString(4, name);
}
}
@Override
protected final void bindValues(SQLiteStatement stmt, Student entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
stmt.bindLong(2, entity.getStudentId());
stmt.bindLong(3, entity.getAge());
String name = entity.getName();
if (name != null) {
stmt.bindString(4, name);
}
}
}
可以看到,這裏對Student
的所有字段使用對應的數據庫語句進行了綁定操作。對於最後的插入操作executeInsert()
, 不同的statement有不同實現,如果當前數據庫是加密型時,則會使用DatabaseStatement
的加密實現類EncryptedDatabaseStatement
應用代理模式去使用sqlcipher
這個加密型數據庫的executeInsert
方法。
7. 刪除
private void deleteFromDB(Student s) {
GreenDAOApplication.getDaoSession().delete(s);
}
我們繼續分析刪除的源碼實現
//@AbstractDaoSession.java
public <T> void delete(T entity) {
@SuppressWarnings("unchecked")
//-->6.1獲取ClasseDao實例
AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
//-->7.1 通過ClassDao刪除對象
dao.delete(entity);
}
//6.1 通過ClassDao刪除對象 @AbstractDao.java
public void delete(T entity) {
assertSinglePk();
//-->7.2 獲取Key
K key = getKeyVerified(entity);
//-->7.3 通過Key刪除數據庫對應數據
deleteByKey(key);
}
//6.2 獲取Key @AbstractDao.java
protected K getKeyVerified(T entity) {
//--> 7.4 獲取key的操作在實現類中,在本例對應的是StudentDao
K key = getKey(entity);
...
return key;
...
}
//6.4 獲取key的實現 @StudentDao.java
@Override
public Long getKey(Student entity) {
if(entity != null) {
return entity.getId();
} else {
return null;
}
}
//7.3 通過Key刪除數據庫對應數據,改操作和5.3的插入操作executeInsert類似 @AbstractDao.java
public void deleteByKey(K key) {
assertSinglePk();
//創建刪除數據庫的statement,實現基本同6.3.0中插入statement創建
DatabaseStatement stmt = statements.getDeleteStatement();
if (db.isDbLockedByCurrentThread()) {
//-->6.5 如果數據庫被當前線程鎖定,直接執行數據庫操作
synchronized (stmt) {
deleteByKeyInsideSynchronized(key, stmt);
}
} else {
// 如果數據庫未被當前線程鎖定,需要先提交事務在進行操作
db.beginTransaction();
try {
synchronized (stmt) {
deleteByKeyInsideSynchronized(key, stmt);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
//將數據在identityScope中刪除
if (identityScope != null) {
identityScope.remove(key);
}
}
//7.5 刪除據庫操作 @AbstractDao
//stmt根據數據庫類型,分別爲EncryptedDatabaseStatement或者StandardDatabaseStatement
private void deleteByKeyInsideSynchronized(K key, DatabaseStatement stmt) {
//綁定數據
if (key instanceof Long) {
stmt.bindLong(1, (Long) key);
} else if (key == null) {
throw new DaoException("Cannot delete entity, key is null");
} else {
stmt.bindString(1, key.toString());
}
//執行刪除操作
stmt.execute();
}
刪除和插入的操作基本一直,就是傳入的sql
語句有區別。
8. 修改
private void updateDB(Student s) {
GreenDAOApplication.getDaoSession().update(s);
}
接下去是源碼實現:
public <T> void update(T entity) {
//-->6.1獲取ClasseDao實例
AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
//-->8.1 修改
dao.update(entity);
}
代碼已經有一股熟悉的味道~
//@AbstractDao.java
public void update(T entity) {
assertSinglePk();
DatabaseStatement stmt = statements.getUpdateStatement();
if (db.isDbLockedByCurrentThread()) {
synchronized (stmt) {
if (isStandardSQLite) {
//-->8.1
updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true);
} else {
//-->8.2
updateInsideSynchronized(entity, stmt, true);
}
}
} else {
db.beginTransaction();
try {
synchronized (stmt) {
updateInsideSynchronized(entity, stmt, true);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
//8.1 @AbstractDao.java
protected void updateInsideSynchronized(T entity, DatabaseStatement stmt, boolean lock) {
// To do? Check if it's worth not to bind PKs here (performance).
bindValues(stmt, entity);
int index = config.allColumns.length + 1;
K key = getKey(entity);
if (key instanceof Long) {
stmt.bindLong(index, (Long) key);
} else if (key == null) {
throw new DaoException("Cannot update entity without key - was it inserted before?");
} else {
stmt.bindString(index, key.toString());
}
stmt.execute();
//-->8.3
attachEntity(key, entity, lock);
}
//8.2 @AbstractDao.java
protected void updateInsideSynchronized(T entity, SQLiteStatement stmt, boolean lock) {
// To do? Check if it's worth not to bind PKs here (performance).
bindValues(stmt, entity);
int index = config.allColumns.length + 1;
K key = getKey(entity);
if (key instanceof Long) {
stmt.bindLong(index, (Long) key);
} else if (key == null) {
throw new DaoException("Cannot update entity without key - was it inserted before?");
} else {
stmt.bindString(index, key.toString());
}
stmt.execute();
attachEntity(key, entity, lock);
}
//8.3 @AbstractDao.java
protected final void attachEntity(K key, T entity, boolean lock) {
attachEntity(entity);
if (identityScope != null && key != null) {
if (lock) {
identityScope.put(key, entity);
} else {
identityScope.putNoLock(key, entity);
}
}
}
因爲套路上插入刪除基本一致,我就簡單說下,修改操作主要完成:
-
通過
getUpdateStatement
方法獲取修改數據庫sql
語句 -
更新數據庫並更新identityScope
9. 查詢
public List<Student> queryData(String s) {
List<Student> students = GreenDAOApplication.getDaoSession().queryRaw(Student.class,
" where Id = ?", s);
return students;
}
源碼實現:
//@AbstractDao.java
public <T, K> List<T> queryRaw(Class<T> entityClass, String where, String... selectionArgs) {
//-->6.1獲取ClasseDao實例
AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
//-->9.1 查詢數據
return dao.queryRaw(where, selectionArgs);
}
//9.1查詢數據 @AbstractDao.java
public List<T> queryRaw(String where, String... selectionArg) {
//根據查詢語句獲取遊標
Cursor cursor = db.rawQuery(statements.getSelectAll() + where, selectionArg);
//-->9.2加載數據
return loadAllAndCloseCursor(cursor);
}
//9.2 @AbstractDao.java
protected List<T> loadAllAndCloseCursor(Cursor cursor) {
try {
//-->9.3 從遊標位置加載數據
return loadAllFromCursor(cursor);
} finally {
cursor.close();
}
}
//9.3加載數據 @AbstractDao.java
protected List<T> loadAllFromCursor(Cursor cursor) {
...
if (window.getNumRows() == count) {
//如果cursor函數沒有偏差,使用FastCursor進行快速定位
cursor = new FastCursor(window);
useFastCursor = true;
}
...
if (cursor.moveToFirst()) {
...
try {
if (!useFastCursor && window != null && identityScope != null) {
//最終調用list.add(loadCurrent(...))
loadAllUnlockOnWindowBounds(cursor, window, list);
} else {
do {
//9.4 加載遊標標記位置數據
list.add(loadCurrent(cursor, 0, false));
} while (cursor.moveToNext());
}
} finally {
if (identityScope != null) {
identityScope.unlock();
}
}
}
return list;
}
// 9.4 獲取數據 @AbstractDao.java
final protected T loadCurrent(Cursor cursor, int offset, boolean lock) {
if (identityScopeLong != null) {
...
//獲取緩存數據
T entity = lock ? identityScopeLong.get2(key) : identityScopeLong.get2NoLock(key);
if (entity != null) {
//如果有緩存,直接返回
return entity;
} else {
//-->9.5 獲取數據並封裝到實例對象中,這裏的實現在StudentDao中
entity = readEntity(cursor, offset);
attachEntity(entity);
//對數據進行緩存
if (lock) {
identityScopeLong.put2(key, entity);
} else {
identityScopeLong.put2NoLock(key, entity);
}
return entity;
}
} else if (identityScope != null) {
K key = readKey(cursor, offset);
if (offset != 0 && key == null) {
// Occurs with deep loads (left outer joins)
return null;
}
T entity = lock ? identityScope.get(key) : identityScope.getNoLock(key);
if (entity != null) {
return entity;
} else {
entity = readEntity(cursor, offset);
attachEntity(key, entity, lock);
return entity;
}
} else {
// Check offset, assume a value !=0 indicating a potential outer join, so check PK
if (offset != 0) {
K key = readKey(cursor, offset);
if (key == null) {
// Occurs with deep loads (left outer joins)
return null;
}
}
T entity = readEntity(cursor, offset);
attachEntity(entity);
return entity;
}
}
// 9.5 將查詢數據封裝到實例對象中 @StudentDao.java
@Override
public Student readEntity(Cursor cursor, int offset) {
Student entity = new Student( //
cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
cursor.getInt(offset + 1), // studentId
cursor.getInt(offset + 2), // age
cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3) // name
);
return entity;
}
首先,如果有實體數據緩存identityScopeLong
/identityScope
,則先從緩存中取,如果緩存中沒有,會使用cursor進行查詢,並將數據封裝到實例對象中,然後緩存到對應的identityScopeLong
/identityScope
,方便下次快速查詢。
10.自動生成代碼
Android中使用比較常見的註解處理器是APT,但是GreenDao使用的是JDT。發中常見的註解處理器有反射、APT、JDT等。反射在java開發中是比較常見的、apt是android開發經常使用的方式、JDT是eclipse開發工具使用的處理器。GreenDao使用的代碼生成模板爲freemarker
開源框架。詳細流程如下:
該圖來自:GreenDAO系列(二) GreenDao 代碼生成策略分析,有興趣的童鞋可以點擊查看。
小結
在分析完GreenDao的核心源碼之後,我們可以發現GreenDao作爲流行數據庫框架有如下特點:
- 內部提供了實體數據的映射緩存機制,能夠加快查詢速度
- 使用代理模式進行了封裝,對不同數據庫進行適配
- 它使用了
sqlcipher
提供了加密數據庫的功能 - 使用
freemarker
模板生成所需的靜態代碼,是框架易用性更高
參考