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來進行數據庫操作,提高了開發效率。
ORM

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繼承自OpenHelperOpenHelper繼承自DatabaseOpenHelperDatabaseOpenHelper繼承自原生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);
        }
    }

可以看到DevOpenHelperupgrade的時候會丟棄所有表,並新建所有表

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

}

StandardDatabaseEncryptedDatabase均實現了 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步操作:

  1. 記錄當前的數據庫對象和版本
  2. 創建daoConfigMapHashMap對象,用於保存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構造函數,這個函數會對我們後面用到的幾個參數進行初始化,包括:isStandardSQLiteidentityScopeidentityScopeLongstatementspkOrdinal,後面用到的時候大家就知道在這裏初始化的。

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()主要完成的工作包括:

  1. 創建了Class實體與Dao對象的映射集合

  2. daoConfigMap中獲取Config對象

  3. 根據IdentityScopeType的類型初始化IdentityScope對象,根據type的不同,它有兩種類型,分別是IdentityScopeObjectIdentityScopeLong,它的作用是根據主鍵緩存對應的實體數據。當主鍵是數字類型的時候,如long/Longint/Integershort/Shortbyte/Byte,使用IdentityScopeLong緩存實體數據,當主鍵不是數字類型的時候,則使用IdentityScopeObject緩存實體數據

  4. 創建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方法中,主要做了兩件事:

  1. 使用SqlUtils創建了插入的sql語句。
  2. 根據不同的數據庫類型(標準數據庫或加密數據庫)將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);
            }
        }
    }

因爲套路上插入刪除基本一致,我就簡單說下,修改操作主要完成:

  1. 通過getUpdateStatement方法獲取修改數據庫sql語句

  2. 更新數據庫並更新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的核心源碼之後,我們可以發現GreenDao作爲流行數據庫框架有如下特點:

  1. 內部提供了實體數據的映射緩存機制,能夠加快查詢速度
  2. 使用代理模式進行了封裝,對不同數據庫進行適配
  3. 它使用了sqlcipher提供了加密數據庫的功能
  4. 使用freemarker模板生成所需的靜態代碼,是框架易用性更高

參考

Android GreenDao 使用全面講解

GreenDao官網

GreenDAO系列(二) GreenDao 代碼生成策略分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章