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 初始化identityScope@DaoConfig.java
   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 代码生成策略分析

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值