greendao源码、流程、优点全面解析

greendao数据库框架是目前我经常在使用的框架,但一直没有真正的仔细的去研究,网上的一些资料感觉都没有总结全面,看完之后总是没有形成体系。所以在此自己总结一篇。

greendao数据库框架的优点可以总结为:

1.crud的性能高效
2.支持数据库加密来保存敏感数据
3.库的体积小
4.可以进行同步和异步操作 5支持内存缓存,提高查询性能
6.代码自动生成,使用简单,无需关注实体类
7.可以和rxjava搭配使用
8.框架开源
9。是一个对象关系映射型数据库框架,可以通过操作对象的方式来对关系型数据库进行操作。

以下分为三个部分进行说明:

  • 核心类解析
  • 工作流程
  • 框架优点详细说明

数据库初始化

//初始化OpenHelper 
 DaoMaster.DevOpenHelper helper=new DaoMaster.DevOpenHelper(this,"db_demo",null);
        SQLiteDatabase db=helper.getWritableDatabase();
        //构建DaoMaster
        DaoMaster daoMaster=new DaoMaster(db);
        //构建Session
        daoSession=daoMaster.newSession();

框架核心类分析

DevOpenHelper:
它是一个静态内部类,继承于OpenHelper ,它重写了onUpgrade方法,主要用来进行版本更新操作。

源码

 public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name) {
            super(context, name);
        }

        public DevOpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory);
        }

        @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 void dropAllTables(Database db, boolean ifExists) {
        FeaturesBeanDao.dropTable(db, ifExists);
        RecordBeanDao.dropTable(db, ifExists);
        RecordPeopleBeanDao.dropTable(db, ifExists);
        UserBeanDao.dropTable(db, ifExists);
    }
//对数据表进行重新创建
  public static void createAllTables(Database db, boolean ifNotExists) {
        FeaturesBeanDao.createTable(db, ifNotExists);
        RecordBeanDao.createTable(db, ifNotExists);
        RecordPeopleBeanDao.createTable(db, ifNotExists);
        UserBeanDao.createTable(db, ifNotExists);
    }

因为DevOpenHelper默认的版本更新会对原来的数据表进行删除,这样的情况会造成原有的数据丢失,所以一般建议使用自定义的类继承OpenHelper来对onUpgrade方法进行重写,从而避免在版本更新的过程中造成数据的丢失,可以使用MigrationHelper开源的类来完成数据库的升级,我自己想到的方法是在onUpgrade内将原来的表置为临时表,并创建新的表单,把临时表中的数据移到新表单中后再将临时表删除,或者可以将原来的表单中的数据先读取到缓存,然后删除原来的数据表,等创建新表单后再将缓存中的数据放入新表单,这样虽然能完成任务,但是性能不友好,所以还是建议直接使用MigrationHelper比较方便。

OpenHelper:
DaoMaster中的静态内部类,它继承于DatabaseOpenHelper ,主要作用是传入当前版本号以及创建表。

   public static abstract class OpenHelper extends DatabaseOpenHelper {
        public OpenHelper(Context context, String name) {
        //将版本号传递给父类
            super(context, name, SCHEMA_VERSION);
        }

        public OpenHelper(Context context, String name, CursorFactory factory) {
          //将版本号传递给父类
            super(context, name, factory, SCHEMA_VERSION);
        }

        @Override
        public void onCreate(Database db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
            //创建表
            createAllTables(db, false);
        }
    }

DatabaseOpenHelper:
继承于SQLiteOpenHelper,在其内部使用代理模式的思想使用StandardDatabaseSQLiteDatabase进行代理操作。

  protected Database wrap(SQLiteDatabase sqLiteDatabase) {
        return new StandardDatabase(sqLiteDatabase);
    }

这2者都实现了Database接口,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();
}

Database 中主要关注2个方法,exexSQL方法和rawQuery方法,前者用于执行SQL语句,后者用于查询记录

DatabaseOpenHelper的另外一个作用就是使用sqlcipher来对数据库进行加密操作,当需要加密时可以使用。

   private class EncryptedHelper extends net.sqlcipher.database.SQLiteOpenHelper {
        public EncryptedHelper(Context context, String name, int version, boolean loadLibs) {
            super(context, name, null, version);
            if (loadLibs) {
                net.sqlcipher.database.SQLiteDatabase.loadLibs(context);
            }
        }

        @Override
        public void onCreate(net.sqlcipher.database.SQLiteDatabase db) {
            DatabaseOpenHelper.this.onCreate(wrap(db));
        }

        @Override
        public void onUpgrade(net.sqlcipher.database.SQLiteDatabase db, int oldVersion, int newVersion) {
            DatabaseOpenHelper.this.onUpgrade(wrap(db), oldVersion, newVersion);
        }

        @Override
        public void onOpen(net.sqlcipher.database.SQLiteDatabase db) {
            DatabaseOpenHelper.this.onOpen(wrap(db));
        }

        protected Database wrap(net.sqlcipher.database.SQLiteDatabase sqLiteDatabase) {
            return new EncryptedDatabase(sqLiteDatabase);
        }

    }

StandardDatabase:
是SQLiteDatabase的代理类,
在这里插入图片描述

DaoMaster:
继承于AbstractDaoMaster,它的功能有:
1.创建所有表和删除所有表

//创建所有表
  public static void createAllTables(Database db, boolean ifNotExists) {
        FeaturesBeanDao.createTable(db, ifNotExists);
        RecordBeanDao.createTable(db, ifNotExists);
        RecordPeopleBeanDao.createTable(db, ifNotExists);
        UserBeanDao.createTable(db, ifNotExists);
    }
//删除所有表
    /** Drops underlying database table using DAOs. */
    public static void dropAllTables(Database db, boolean ifExists) {
        FeaturesBeanDao.dropTable(db, ifExists);
        RecordBeanDao.dropTable(db, ifExists);
        RecordPeopleBeanDao.dropTable(db, ifExists);
        UserBeanDao.dropTable(db, ifExists);
    }

2.创建Session会话

   public DaoSession newSession() {
        return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
    }

    public DaoSession newSession(IdentityScopeType type) {
        return new DaoSession(db, type, daoConfigMap);
    }

3.将SQLiteDatabase的代理类StandardDatabase进行保存以及对所有的DAO通过registerDaoClass()进行注册,以便DaoMaster对其进行管理。

//
  protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
  	//DaoConfig 用来获取DAO里面的Properties 所有static或者public字段
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        //以daoClass为KEY,使用map的方式对DaoConfig 进行管理
        daoConfigMap.put(daoClass, daoConfig);
    }

DaoConfig 初始化的时候会对这些属性进行保存,

public final class DaoConfig implements Cloneable {

    public final Database db;  //数据库持有对象
    public final String tablename;  //数据库的名称
    public final Property[] properties; //属性
    public final String[] allColumns;  //所有行
    public final String[] pkColumns;  //具备主键的行
    public final String[] nonPkColumns;  //不具备主键的行
    public final Property pkProperty;
    public final boolean keyIsNumeric;   //主键是否是数字
    public final TableStatements statements;  //描述crud的数据表语句
    private IdentityScope<?, ?> identityScope;  //是否需要内存缓存策略

这些属性都会在DaoConfig 进行实例化的时候进行设置

 public DaoConfig(Database db, Class<? extends AbstractDao<?, ?>> daoClass) {
        this.db = db;
        try {
            this.tablename = (String) daoClass.getField("TABLENAME").get(null);
            //通过反射的方式获得属性对象
            Property[] properties = reflectProperties(daoClass);
            this.properties = properties;

            allColumns = new String[properties.length];

            List<String> pkColumnList = new ArrayList<String>();
            List<String> nonPkColumnList = new ArrayList<String>();
            Property lastPkProperty = null;
            for (int i = 0; i < properties.length; i++) {
                Property property = properties[i];
                String name = property.columnName;
                allColumns[i] = name;
                if (property.primaryKey) {
                    pkColumnList.add(name);
                    lastPkProperty = property;
                } else {
                    nonPkColumnList.add(name);
                }
            }
            String[] nonPkColumnsArray = new String[nonPkColumnList.size()];
            nonPkColumns = nonPkColumnList.toArray(nonPkColumnsArray);
            String[] pkColumnsArray = new String[pkColumnList.size()];
            pkColumns = pkColumnList.toArray(pkColumnsArray);

            pkProperty = pkColumns.length == 1 ? lastPkProperty : null;
            statements = new TableStatements(db, tablename, allColumns, pkColumns);

	//判断主键是否是数字
            if (pkProperty != null) {
                Class<?> type = pkProperty.type;
                keyIsNumeric = type.equals(long.class) || type.equals(Long.class) || type.equals(int.class)
                        || type.equals(Integer.class) || type.equals(short.class) || type.equals(Short.class)
                        || type.equals(byte.class) || type.equals(Byte.class);
            } else {
                keyIsNumeric = false;
            }

        } catch (Exception e) {
            throw new DaoException("Could not init DAOConfig", e);
        }
    }

//此方法用来设置是否启用内存缓存

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

4.定义内部类OpenHelperDevOpenHelper
在这里插入图片描述

DaoSession:
继承于AbstractDaoSession,它的作用主要有以下几点

1.DaoSession会创建我们需要的Dao对象并注册到映射表中管理起来,以及从DaoMaster中的daoConfigMap中获取到daoConfig,并将daoConfigsession传递到xxDao对象中去

  public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        super(db);
 
        //根据Class<?>取出配置信息
        noteDaoConfig = daoConfigMap.get(NoteDao.class).clone();
        //根据Session的缓存类型初始化定制的缓存范围
        noteDaoConfig.initIdentityScope(type);
  //根据配置初始化得到一个NoteDao实体
        noteDao = new NoteDao(noteDaoConfig, this);
        //注册到映射表中
        registerDao(Note.class, noteDao);
    }
    

这个方法在AbstractDaoSession之中,会通过map对映射进行管理

    protected <T> void registerDao(Class<T> entityClass, AbstractDao<T, ?> dao) {
        entityToDao.put(entityClass, dao);
    }

2.通过get方法获取对应的dao对象

 public XXDao getxxDao() {
        return xxDao;
    }

3.清空内存缓存

public void clear() {
        noteDaoConfig.clearIdentityScope();
    }

AbstractDaoSession:
提供了crud操作,可以通过映射表来获得dao直接进行crud操作
在这里插入图片描述
通过这种方式操作和调用session提供的get方法获取到dao对象之后没有差别

  public <T> long insert(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        return dao.insert(entity);
    }

实体xxEntity类:
需要用@Entity进行修饰才可以自动生成相应的代码,以及对应的dao

@Entity
public class RecordBean {

    @Id()
    private String Uid;

    @NotNull
    private String id;
   
    @NotNull
    private String name;

    @NotNull
    private Long UNIX_time;

    private String time;

    @NotNull
    private String manchineId;

Dao类:
主要作了以下几点工作:

对应的Dao类都继承AbstractDao

public class RecordBeanDao extends AbstractDao<RecordBean, String> 

1.每个实体Dao都有一个Properties来管理对象实体属性对表各列的映射关系,对像为Property
它主要用来做2个工作:querybuilder使用他去创建WhereCondition对象以及

DaoMaster初始化时,DaoConfig对象内部变量的赋值

  public static class Properties {
        public final static Property Uid = new Property(0, String.class, "Uid", true, "UID");
        public final static Property Id = new Property(1, String.class, "id", false, "ID");
        public final static Property Name = new Property(2, String.class, "name", false, "NAME");
        public final static Property UNIX_time = new Property(3, Long.class, "UNIX_time", false, "UNIX_TIME");
        public final static Property Time = new Property(4, String.class, "time", false, "TIME");
        public final static Property ManchineId = new Property(5, String.class, "manchineId", false, "MANCHINE_ID");
    }

2.提供了创建表和删除表的实现

  public RecordBeanDao(DaoConfig config, DaoSession daoSession) {
        super(config, daoSession);
    }

 //进行创建表
    public static void createTable(Database db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"RECORD_BEAN\" (" + //
                "\"UID\" TEXT PRIMARY KEY NOT NULL ," + // 0: Uid
                "\"ID\" TEXT NOT NULL ," + // 1: id
                "\"NAME\" TEXT NOT NULL ," + // 2: name
                "\"UNIX_TIME\" INTEGER NOT NULL ," + // 3: UNIX_time
                "\"TIME\" TEXT," + // 4: time
                "\"MANCHINE_ID\" TEXT NOT NULL );"); // 5: manchineId
    }

  	//对表进行删除操作
    public static void dropTable(Database db, boolean ifExists) {
        String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"RECORD_BEAN\"";
        db.execSQL(sql);
    }

3.实现了bindValues方法,

    @Override
    protected final void bindValues(SQLiteStatement stmt, RecordPeopleBean entity) {
        stmt.clearBindings();
 
        Long numId = entity.getNumId();
        if (numId != null) {
            stmt.bindLong(1, numId);
        }
 
        String userId = entity.getUserId();
        if (userId != null) {
            stmt.bindString(2, userId);
        }
        stmt.bindString(3, entity.getName());
        stmt.bindString(4, entity.getPhone());
        stmt.bindString(5, entity.getData());
        stmt.bindLong(6, entity.getUploadBool());
    }

AbstractDao封装了和数据库进行crud的操作

操作流程

以插入为例,这是一串简单的演示代码,通过这个操作就可以将一个UserBean类型的数据插入到数据库之中

 daoMaster.newSession().getUserBeanDao().insert(new UserBean());

通过daoMaster.newSession().getUserBeanDao()可以获取到UserBeanDao对象
然后调用它的insert方法最终会调用到他的父类AbstractDao类中的insert方法

entity是我们插入的数据,statements.getInsertStatement()是我们通过占位符的Sql语句获取一个SQLiteStatement并包装成的DatabaseStatement 。这个参数是通过AbstractDao保存的xxDao对应的daoConfig中的TableStatements获取到的。

AbstractDao会根据执行不同的增删改查,从而调用相应的方法获取对应的DatabaseStatementSQLiteStatement 的静态代理

  public long insert(T entity) {
        return executeInsert(entity, statements.getInsertStatement(), true);
    }

daoConfig初始化的时候会实例化该对象

  statements = new TableStatements(db, tablename, allColumns, pkColumns);

最终会走到executeInsert中去

    private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
        long rowId;
        //先判断当前线程是否连接了数据库
        if (db.isDbLockedByCurrentThread()) {
        //返回true 直接插入数据
            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;
    }
 public DatabaseStatement getInsertOrReplaceStatement() {
        if (insertOrReplaceStatement == null) {
            String sql = SqlUtils.createSqlInsert("INSERT OR REPLACE INTO ", tablename, allColumns);
            DatabaseStatement newInsertOrReplaceStatement = db.compileStatement(sql);
            synchronized (this) {
                if (insertOrReplaceStatement == null) {
                    insertOrReplaceStatement = newInsertOrReplaceStatement;
                }
            }
            if (insertOrReplaceStatement != newInsertOrReplaceStatement) {
                newInsertOrReplaceStatement.close();
            }
        }
        return insertOrReplaceStatement;
    }

插入操作主要调用了这个方法

private long insertInsideTx(T entity, DatabaseStatement stmt) {
    synchronized (stmt) {
        if (isStandardSQLite) {
        //获取源数据库rawStmt 
            SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
            //然后执行子类的bindValues方法,进行绑定值后执行Sql操作
            bindValues(rawStmt, entity);
            //然后将数据插入到数据库中
            return rawStmt.executeInsert();
        } else {
            bindValues(stmt, entity);
            return stmt.executeInsert();
        }
    }
}

条件语句使用
对于条件语句则是通过QueryBuilder构造查询条件,我们通过根据条件删除来进行分析

getDaoInstant().getRecordPeopleBeanDao().queryBuilder().where(RecordPeopleBeanDao.Properties.UploadBool.eq(updataBool)).buildDelete();

我们可以通过getXXDao方法获取到queryBuilder对象

  public QueryBuilder<T> queryBuilder() {
        return QueryBuilder.internalCreate(this);
    }

官方注释解释为使用AND将给定条件添加到where子句。其实这一步主要做的动作是将条件放入到whereCollector中的一个list集合中去,然后在具体调用buildDelete时再从这个集合中拿出来进行拼接

  public QueryBuilder<T> where(WhereCondition cond, WhereCondition... condMore) {
        whereCollector.add(cond, condMore);
        return this;
    }

QueryBuilder提供了相应的增删改查方法,然后此时调用的是QueryBuilder提供的方法,如下调用的buildDeleteQueryBuilder中的 方法

queryBuilder().where(RecordPeopleBeanDao.Properties.UploadBool.eq(updataBool)).buildDelete();
   public DeleteQuery<T> buildDelete() {
        if (!joins.isEmpty()) {
            throw new DaoException("JOINs are not supported for DELETE queries");
        }
        String tablename = dao.getTablename();
        String baseSql = SqlUtils.createSqlDelete(tablename, null);
        StringBuilder builder = new StringBuilder(baseSql);

        // tablePrefix gets replaced by table name below. Don't use tableName here because it causes trouble when
        // table name ends with tablePrefix.
        //拼接sql
        appendJoinsAndWheres(builder, tablePrefix);

        String sql = builder.toString();
        // Remove table aliases, not supported for DELETE queries.
        // TODO(?): don't create table aliases in the first place.
        sql = sql.replace(tablePrefix + ".\"", '"' + tablename + "\".\"");
        checkLog(sql);
		//根据条件执行删除,并返回结果
        return DeleteQuery.create(dao, sql, values.toArray());
    }

这里要说的是appendJoinsAndWheres,他的主要作用就是拼接where语句

    private void `appendJoinsAndWheres`(StringBuilder builder, String tablePrefixOrNull) {
        values.clear();
        for (Join<T, ?> join : joins) {
            builder.append(" JOIN ").append(join.daoDestination.getTablename()).append(' ');
            builder.append(join.tablePrefix).append(" ON ");
            SqlUtils.appendProperty(builder, join.sourceTablePrefix, join.joinPropertySource).append('=');
            SqlUtils.appendProperty(builder, join.tablePrefix, join.joinPropertyDestination);
        }
        boolean whereAppended = !whereCollector.isEmpty();
        if (whereAppended) {
            builder.append(" WHERE ");
            whereCollector.appendWhereClause(builder, tablePrefixOrNull, values);
        }
        for (Join<T, ?> join : joins) {
            if (!join.whereCollector.isEmpty()) {
                if (!whereAppended) {
                    builder.append(" WHERE ");
                    whereAppended = true;
                } else {
                    builder.append(" AND ");
                }
                join.whereCollector.appendWhereClause(builder, join.tablePrefix, values);
            }
        }
    }

greendao的各项优点在代码中的体现

1.crud的性能高效
当我们使用greendao对数据库进行增删改查时,会根据不同的操作调用daoConfigTableStatements的相应方法获得DatabaseStatement

DatabaseStatement是对SQLiteStatement的静态代理
greenDAO直接通过DatabaseStatement获得了SQLiteStatement并进行使用,而没有调用SQLite开放给我们的API,从而节省了代码,增加了速度。

例如insert操作

    private long insertInsideTx(T entity, DatabaseStatement stmt) {
        synchronized (stmt) {
            if (isStandardSQLite) {
            //直接从DatabaseStatement 中获取到SQLiteStatement 
                SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
                bindValues(rawStmt, entity);
                return rawStmt.executeInsert();
            } else {
                bindValues(stmt, entity);
                return stmt.executeInsert();
            }
        }
    }

缓存机制的支持以及通过DaoMaster、Session避免了数据库操作对象的频繁创建

2.支持数据库加密来保存敏感数据
数据库的加密操作是通过sqlcipher这个库来实现的,此内部类在DatabaseOpenHelper中。

 private class EncryptedHelper extends net.sqlcipher.database.SQLiteOpenHelper {
        public EncryptedHelper(Context context, String name, int version, boolean loadLibs) {
            super(context, name, null, version);
            if (loadLibs) {
                net.sqlcipher.database.SQLiteDatabase.loadLibs(context);
            }
        }

        @Override
        public void onCreate(net.sqlcipher.database.SQLiteDatabase db) {
            DatabaseOpenHelper.this.onCreate(wrap(db));
        }

        @Override
        public void onUpgrade(net.sqlcipher.database.SQLiteDatabase db, int oldVersion, int newVersion) {
            DatabaseOpenHelper.this.onUpgrade(wrap(db), oldVersion, newVersion);
        }

        @Override
        public void onOpen(net.sqlcipher.database.SQLiteDatabase db) {
            DatabaseOpenHelper.this.onOpen(wrap(db));
        }

        protected Database wrap(net.sqlcipher.database.SQLiteDatabase sqLiteDatabase) {
            return new EncryptedDatabase(sqLiteDatabase);
        }

    }

使用如下方法对数据库进行加密,如果没加密,会使用Android的Sqlite API去操作数据库,如果加密,则使用sqlcipher提供的API去操作数据库。

DaoMaster.DevOpenHelper mDevOpenHelper = new DaoMaster.DevOpenHelper(context, dbName);
DaoSession mDaoSession = new DaoMaster(mDevOpenHelper.getEncryptedWritableDb(passwprd)).newSession();

3.库的体积小
体积小于150K

4.可以进行同步和异步操作
用法:

AsyncSession asyncSession = daoSession.startAsyncSession();
asyncSession.setListener(new AsyncOperationListener() {
   @Override
   public void onAsyncOperationCompleted(AsyncOperation operation) {
       AsyncOperation.OperationType type = operation.getType();
       Log.e(TAG, type.name());
   }
});
asyncSession.insert(note);

session给我们提供了异步操作对象AsyncSession,通过startAsyncSession可以获取的到,我们获取一个AsyncSession对象进行异步的数据库操作,并且可以设置AsyncOperation对异步操作进行监听。

    public AsyncSession startAsyncSession() {
        return new AsyncSession(this);
    }

查看AsyncSession的insert方法

public AsyncOperation insert(Object entity) {
    return insert(entity, 0);
}

最后可以走到enqueEntityOperation方法,这个方法主要是构建了AsyncOperation
来保存我们进行异步操作所必要的信息,并传递到executor.enqueue中去执行

AbstractDao<?, ?> dao = daoSession.getDao(entityClass);
AsyncOperation operation = new AsyncOperation(type, dao, null, param, flags | sessionFlags);
executor.enqueue(operation);
return operation;

这个enqueueAsyncOperationExecutor中的方法,AsyncOperationExecutor实现了runable接口,并且内部定义了一个线程池。

   public void enqueue(AsyncOperation operation) {
        synchronized (this) {
            operation.sequenceNumber = ++lastSequenceNumber;
            //将operation添加到队列中去,在执行run方法时会从从队列中获取
            queue.add(operation);
            countOperationsEnqueued++;
            if (!executorRunning) {
                executorRunning = true;
                //开启线程池
                executorService.execute(this);
            }
        }
    }

跟入run方法,主要代码如下,每次会从队列中拿一个异步操作对象执行逻辑,最终会调用executeOperationAndPostCompleted

      if (operation.isMergeTx()) {
                        // Wait some ms for another operation to merge because a TX is expensive
                        AsyncOperation operation2 = queue.poll(waitForMergeMillis, TimeUnit.MILLISECONDS);
                        if (operation2 != null) {
                            if (operation.isMergeableWith(operation2)) {
                                mergeTxAndExecute(operation, operation2);
                            } else {
                                // Cannot merge, execute both
                                executeOperationAndPostCompleted(operation);
                                executeOperationAndPostCompleted(operation2);
                            }
                            continue;
                        }
                    }
                    executeOperationAndPostCompleted(operation);

executeOperationAndPostCompleted这个方法,调用了2个方法

  private void executeOperationAndPostCompleted(AsyncOperation operation) {
  //根据operation.type,调用xxDao相应的增删改查方法
        executeOperation(operation);
        //将operation发送到主线程去执行
        handleOperationCompleted(operation);
    }

通过这一系列操作,就成功的进行了异步数据库操作

5支持内存缓存,提高查询性能
当插入数据的时候会从IdentityScope插入数据,当查询数据的时候会从IdentityScope获取数据

我们会在插入的时候对setKeyAndAttach进行判断,如果设置了则进入updateKeyAfterInsertAndAttach方法

 if (setKeyAndAttach) {
            updateKeyAfterInsertAndAttach(entity, rowId, true);
        }

setKeyAndAttach中的值是通过 isEntityUpdateable() 方法获得,这个方法由子类重写

 protected final boolean isEntityUpdateable() {
        return true;
    }

最终会走到attachEntity中,在这个方法内真正执行了缓存数据,通过identityScope来进行保存

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

查询的时候也会根据是否设置缓存从identityScope中进行获取

 if (identityScope != null) {
            T entity = identityScope.get(key);
            if (entity != null) {
                return entity;
            }
        }

如果要清空缓存使用Session.clear即可

daoSession.clear();

6.代码自动生成,使用简单,无需关注实体类
greenDAO通过freemarker模板方式在编译期之前帮你生成可用的和数据库生成有关的表帮助对象,提升了运行期的速度

7.可以和rxjava搭配使用

RxDao<xxEntity, Long> xxDao = daoSession.getXXDao().rx();
xxDao.insert(xxEntity)
    .observerOn(AndroidSchedules.mainThread())
    .subscribe(new Action1<xxEntity>() {
        @Override
        public void call(xxEntity entity) {
            // insert success
        }
    })

查看AbstractDao中的rx方法,它返回了一个rxDao对象

   public RxDao<T, K> rx() {
        if (rxDao == null) {
            rxDao = new RxDao<>(this, Schedulers.io());
        }
        return rxDao;
    }

调用它的insert方法会调用dao的插入逻辑并返回一个Observable,就可以将其作为rxjava的上游进行使用

    public Observable<T> insert(final T entity) {
        return wrap(new Callable<T>() {
            @Override
            public T call() throws Exception {
                dao.insert(entity);
                return entity;
            }
        });
    }

8.是一个对象关系映射型框架,可以通过操作对象的方式来对关系型数据库进行操作。greendao通过代理对象来调用sqlite的相应方法
通过StandardDatabase对SQLiteDatabase静态代理,在创建OpenHelper时会对其进行代理。使用DatabaseStatementSQLiteStatement进行静态代理

猜你喜欢

转载自blog.csdn.net/qq_24856205/article/details/103712129