美文网首页源码分析
GreenDao源码分析

GreenDao源码分析

作者: h2coder | 来源:发表于2022-04-21 23:50 被阅读0次

前言

GreenDao和Room都是Android上常用的数据库框架,本篇来分析一下GreenDao的源码。

简单使用

添加依赖

//1、根build.gradle,添加GreenDao插件依赖
buildscript {
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.6.4'
        //GreenDao
        classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

//2、app模块的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'

3、引入Gradle插件以及依赖
dependencies {
    //GreenDao
    implementation 'org.greenrobot:greendao:3.3.0'
    //可选,加密型数据库依赖
    implementation 'net.zetetic:android-database-sqlcipher:3.5.9'
}

4、配置GreenDao,可选
greendao {
    schemaVersion 1
    //指定生成的类的目录,默认生成在build目录中
    daoPackage 'com.hzh.green.dao.sample.db'
    targetGenDir 'src/main/java'
}

创建数据库表实体

  • @Entity注解,声明一个实体类为数据库表实体,nameInDb属性可指定对应的表名
  • @Id注解,声明某个属性是这个表的主键
  • @Property注解,声明实体的某个成员属性对应表的某个字段名,nameInDb属性可指定表的字段名
  • @NotNull注解,声明这个字段为非空
@Entity(nameInDb = "person_info")
public class PersonInfoEntity {
    @Id
    private String id;

    @Property(nameInDb = "create_time")
    @NotNull
    private Date createTime = new Date();

    @Property(nameInDb = "update_time")
    @NotNull
    private Date updateTime = new Date();

    /**
     * 删除标识,0:未删除,1:删除
     */
    @Property(nameInDb = "delete_flag")
    @NotNull
    private Integer deleteFlag = 0;

    /**
     * 版本号,默认1
     */
    @Property(nameInDb = "version")
    @NotNull
    private Integer version = 1;

    //-------------------- 自定义属性 --------------------

    @Property(nameInDb = "person_name")
    @NotNull
    private String personName;

    @Property(nameInDb = "sex")
    @NotNull
    private String sex;

    @Property(nameInDb = "age")
    @NotNull
    private Integer age;
    
    //省略getter、setter方法
}

生成Dao

写完实体后,手动build一下,在项目的build => generated => source => greendao => 包名下,会生成这几个类,分别是:DaoMasterDaoSessionPersonInfoEntityDao

  • DaoMaster,数据库层的总入口,Dao的主人以及负责生辰DaoSession,内部有2个抽象类,OpenHelperDevOpenHelperOpenHelper继承于DatabaseOpenHelper,而DevOpenHelper继承于OpenHelper,而DatabaseOpenHelper又继承于Android的SQLiteOpenHelper,并重写了它的某些方法
  • DaoSession,会话层,用于生成Dao、注册Dao、以及Dao的CRUD方法
  • Dao,数据库实体对应的Dao层对象,拥有表的配置信息,也有部分的CRUD方法

开始使用

  • 自定义SQLiteOpenHelper

该类继承于DaoMaster.OpenHelper,处理数据库版本升级

public class SampleSQLiteOpenHelper extends DaoMaster.OpenHelper {
    private static final String TAG = SampleSQLiteOpenHelper.class.getSimpleName();

    public SampleSQLiteOpenHelper(Context context, String name) {
        super(context, name);
    }

    public SampleSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        super.onUpgrade(db, oldVersion, newVersion);
        if (oldVersion == newVersion) {
            Log.i(TAG, "数据库无需升级");
            return;
        }
        Log.i(TAG, "数据库从" + oldVersion + "升级到 ::: " + newVersion + "版本");
        //处理升级...
    }
}
  • 创建单例类GreenDaoManager,获取DaoMaster和DaoSession
public class GreenDaoManager {
    private static final String DB_NAME = "app_db";
    private static volatile GreenDaoManager mInstance;
    private DaoMaster mDaoMaster;
    private DaoSession mDaoSession;

    private GreenDaoManager() {
        if (mInstance == null) {
            SampleSQLiteOpenHelper openHelper = new SampleSQLiteOpenHelper(AppContext.getInstance(), DB_NAME);
            SQLiteDatabase db = openHelper.getWritableDatabase();
            mDaoMaster = new DaoMaster(db);
            mDaoSession = mDaoMaster.newSession();
        }
    }

    public static void init() {
        getInstance();
    }

    public static GreenDaoManager getInstance() {
        if (mInstance == null) {
            synchronized (GreenDaoManager.class) {
                if (mInstance == null) {
                    mInstance = new GreenDaoManager();
                }
            }
        }
        return mInstance;
    }

    public DaoMaster getDaoMaster() {
        return mDaoMaster;
    }

    public DaoSession getDaoSession() {
        return mDaoSession;
    }
}
  • 获取Dao,并进行CRUD
//获取Dao对象
PersonInfoEntityDao dao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao();

//增
dao.insert(entity);

//删
dao.delete(entity);

//查
dao.loadAll();

//改
dao.update(entity);

浅窥代码

DaoMaster,作为数据库框架的总入口,里面存在2个抽象类,分别是OpenHelperDevOpenHelper,其中DevOpenHelper继承于OpenHelperOpenHelper继承于DatabaseOpenHelper,它又继承于Android的SQLiteOpenHelper

  • OpenHelper,在onCreate()中,就是数据库创建时,通过createAllTables(db, ifNotExists),创建所有表,而onUpgrade()没有被重写,开发者需要重写该方法,进行当数据库升级时,表结构的一些变化对应的处理
  • DevOpenHelper,因为继承于OpenHelper,所以onCreate()方法走的也是OpenHelper的创建所有表,但重写了onUpgrade(),并在其中调用dropAllTables(db, ifNotExists)来删除所有表,再调一次onCreate()来重新创建表,所以数据会丢失,一般用于开发过程中使用
public class DaoMaster extends AbstractDaoMaster {
    public static final int SCHEMA_VERSION = 1;

    /** Creates underlying database table using DAOs. */
    //创建表
    public static void createAllTables(Database db, boolean ifNotExists) {
        PersonInfoEntityDao.createTable(db, ifNotExists);
    }

    /** Drops underlying database table using DAOs. */
    //删除所有表
    public static void dropAllTables(Database db, boolean ifExists) {
        PersonInfoEntityDao.dropTable(db, ifExists);
    }
    
    //...

    /**
     * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
     */
    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);
        }
    }

    /** WARNING: Drops all table on Upgrade! Use only during development. */
    //发生数据库升级时,删除所有表,再重建表,所以数据会丢失,一般用于开发过程中使用
    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);
        }
    }
}

DatabaseOpenHelper

DatabaseOpenHelper继承于SQLiteOpenHelper,所以它也是一个数据库帮助类,它提供了获取2种类型的数据库,分别是获取StandardDatabase标准型数据库,获取EncryptedDatabase加密型数据库

这里使用了代理模式,通过wrap()方法,把不同类型数据库的SQLiteDatabase包装为统一的Database接口,分别是StandardDatabaseEncryptedDatabase

public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {
    //...
    
    //获取标准的可写数据库
    public Database getWritableDb() {
        return wrap(getWritableDatabase());
    }
    
    //获取标准的可读数据库
    public Database getReadableDb() {
        return wrap(getReadableDatabase());
    }
    
    //包装,SQLiteDatabase为StandardDatabase,并返回StandardDatabase的基础接口Database
    protected Database wrap(SQLiteDatabase sqLiteDatabase) {
        return new StandardDatabase(sqLiteDatabase);
    }
    
    //获取可读的加密数据库
    public Database getEncryptedWritableDb(String password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.getEncryptedWritableDb(password);
    }
    
    //获取可写的加密数据库
    public Database getEncryptedWritableDb(char[] password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.getEncryptedWritableDb(password);
    }
    
    interface EncryptedHelper {
        Database getEncryptedReadableDb(String password);
        Database getEncryptedReadableDb(char[] password);
        Database getEncryptedWritableDb(String password);
        Database getEncryptedWritableDb(char[] password);
    }
    
    class SqlCipherEncryptedHelper extends net.sqlcipher.database.SQLiteOpenHelper implements DatabaseOpenHelper.EncryptedHelper {
        private Database wrap(SQLiteDatabase sqLiteDatabase) {
            return new EncryptedDatabase(sqLiteDatabase);
        }
    }
    
    //...
}

StandardDatabase、EncryptedDatabase

StandardDatabaseEncryptedDatabase,分别都代理了各自的SQLiteDatabase,统一了API。

加密型数据库,是依赖了sqlcipher第三方加密型数据库。如果需要创建加密型数据库,添加好依赖后,把获取DB对象的getWritableDb()方法,换成getEncryptedWritableDb()即可,无需做其他改动,这就是代理模式的好处

//标准型数据库,StandardDatabase
public class StandardDatabase implements Database {
    //这个SQLiteDatabase,是android.database.sqlite.SQLiteDatabase包下的
    private final SQLiteDatabase delegate;

    public StandardDatabase(SQLiteDatabase delegate) {
        this.delegate = delegate;
    }
    
    @Override
    public Cursor rawQuery(String sql, String[] selectionArgs) {
        return delegate.rawQuery(sql, selectionArgs);
    }

    @Override
    public void execSQL(String sql) throws SQLException {
        delegate.execSQL(sql);
    }
    
    //...
}

//加密型数据库,EncryptedDatabase
public class EncryptedDatabase implements Database {
    //这个SQLiteDatabase,是net.sqlcipher.database包下的
    private final SQLiteDatabase delegate;
    
    public EncryptedDatabase(SQLiteDatabase delegate) {
        this.delegate = delegate;
    }

    @Override
    public Cursor rawQuery(String sql, String[] selectionArgs) {
        return delegate.rawQuery(sql, selectionArgs);
    }

    @Override
    public void execSQL(String sql) throws SQLException {
        delegate.execSQL(sql);
    }
}

源码分析

DaoMaster

DaoMaster继承于AbstractDaoMaster,构造方法需要传入SQLiteDatabase实例,例如通过getWritableDatabase()获取到,传入就可以获取一个DaoMaster实例,内部就会创建一个标准型数据的代理类StandardDatabase,同时通过registerDaoClass()注册Dao

AbstractDaoMaster基类的构造方法中,保存了Database代理类、版本号外,还创建了一个名为daoConfigMap,类型为Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>的HashMap,这个Map负责注册Dao和DaoConfig的映射关系,而后续的DaoSession,就会通过这个daoConfigMap,获取对应Dao的DaoConfig来创建Dao实例

public class DaoMaster extends AbstractDaoMaster {
    //...

    public DaoMaster(SQLiteDatabase db) {
        this(new StandardDatabase(db));
    }

    public DaoMaster(Database db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(PersonInfoEntityDao.class);
    }
    
    //...
}

public abstract class AbstractDaoMaster {
    protected final Database db;
    protected final int schemaVersion;
    protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;
    
    public AbstractDaoMaster(Database db, int schemaVersion) {
        this.db = db;
        this.schemaVersion = schemaVersion;
        daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
    }
    
    protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        daoConfigMap.put(daoClass, daoConfig);
    }
}

DaoSession

DaoSession,作为会话层对象,负责创建Dao、注册Dao,以及提供一些CRUD的能力

通过DaoMasternewSession()方法,就可以创建一个DaoSession实例

//创建DaoSession
DaoSession daoSession = daoMaster.newSession();

public class DaoMaster extends AbstractDaoMaster {
    //...
    
    public DaoSession newSession() {
        return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
    }
    
    //...
}
  • DaoSession的构造方法

DaoSession继承于AbstractDaoSession,DaoSession的构造方法,首先super(db)调用父类的构造方法

AbstractDaoSession的构造方法,创建了一个名为entityToDao,类型为Map<Class<?>, AbstractDao<?, ?>>HashMap,用于注册实体类EntityDao之间的关系

  • 通过Dao类型,获取DaoConfig
  • 根据type类型,初始化IdentityScope
  • 根据DaoConfig,创建Dao实例
  • 注册Dao,将实体类Entity和Dao映射起来

IdentityScope有2种类型,分别是IdentityScopeObjectIdentityScopeLong,它们的作用是根据主键缓存实体类数据,IdentityScopeLong为主键为数字类型时使用,例如int、long等,而IdentityScopeObject则为非数字类型的主键时使用,例如String

public class DaoSession extends AbstractDaoSession {
    private final DaoConfig personInfoEntityDaoConfig;
    private final PersonInfoEntityDao personInfoEntityDao;

    public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        //调用父类的构造方法
        super(db);
        //1、通过Dao类型,获取DaoConfig 
        personInfoEntityDaoConfig = daoConfigMap.get(PersonInfoEntityDao.class).clone();
        //2、根据type类型,初始化IdentityScope
        personInfoEntityDaoConfig.initIdentityScope(type);
        
        //3、根据DaoConfig,创建Dao实例
        personInfoEntityDao = new PersonInfoEntityDao(personInfoEntityDaoConfig, this);
        
        //4、注册Dao,将实体类Entity和Dao映射起来
        registerDao(PersonInfoEntity.class, personInfoEntityDao);
    }
}

//父类AbstractDaoSession
public class AbstractDaoSession {
    private final Database db;
    private final Map<Class<?>, AbstractDao<?, ?>> entityToDao;

    public AbstractDaoSession(Database db) {
        this.db = db;
        this.entityToDao = new HashMap<Class<?>, AbstractDao<?, ?>>();
    }
    
    protected <T> void registerDao(Class<T> entityClass, AbstractDao<T, ?> dao) {
        entityToDao.put(entityClass, dao);
    }
    
    //...
}

插入流程分析

插入一条数据,只需要获取Dao实例,调insert()方法,传入实体对象就可以了

//获取Dao对象
PersonInfoEntityDao dao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao();
//增
dao.insert(entity);

点进insert()方法,发现是Dao的父类AbstractDao的方法,并且里面定义了通用的CRUD方法

public abstract class AbstractDao<T, K> {
    //增
    public long insert(T entity) {
        return executeInsert(entity, statements.getInsertStatement(), true);
    }
    
    //删
    public void delete(T entity) {
        assertSinglePk();
        K key = getKeyVerified(entity);
        deleteByKey(key);
    }
    
    //查
    public List<T> loadAll() {
        Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
        return loadAllAndCloseCursor(cursor);
    }
    
    //改
    public void update(T entity) {
        assertSinglePk();
        DatabaseStatement stmt = statements.getUpdateStatement();
        if (db.isDbLockedByCurrentThread()) {
            synchronized (stmt) {
                if (isStandardSQLite) {
                    updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true);
                } else {
                    updateInsideSynchronized(entity, stmt, true);
                }
            }
        } else {
            // Do TX to acquire a connection before locking the stmt to avoid deadlocks
            db.beginTransaction();
            try {
                synchronized (stmt) {
                    updateInsideSynchronized(entity, stmt, true);
                }
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
    }
}

先来看看insert()方法的流程,在AbstractDao的构造方法中,传入了DaoConfig,它是Dao的配置信息,通过它获取了TableStatements对象,它是一个用于生成CRUD的SQL语句的工具类

  • 插入的SQL语句,在通过TableStatements中的SqlUtils.createSqlInsert()来生成
  • 根据不同类型的数据库,将SQL语句编译成对应的语句
public abstract class AbstractDao<T, K> {
    //...
    
    protected final TableStatements statements;

    @SuppressWarnings("unchecked")
    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;
    }

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

public class TableStatements {
    //...
    
    public DatabaseStatement getInsertStatement() {
        if (insertStatement == null) {
            //--------------------- 重点 ---------------------
            String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns);
            //--------------------- 重点 ---------------------
            //编译对应数据库的Statement
            DatabaseStatement newInsertStatement = db.compileStatement(sql);
            synchronized (this) {
                if (insertStatement == null) {
                    insertStatement = newInsertStatement;
                }
            }
            if (insertStatement != newInsertStatement) {
                newInsertStatement.close();
            }
        }
        return insertStatement;
    }
    
    //...
}

继续看executeInsert()方法

  • 判断数据库是否被当前线程锁定,如是,则直接插入数据
  • 如果没有被锁定,为了避免死锁,开启一个事务,再进行插入
  • 最后,如果有设置主键,则把数据实体缓存到identityScope中
public abstract class AbstractDao<T, K> {
    private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
        long rowId;
        //1、判断数据库是否被当前线程锁定,如是,则直接插入数据
        if (db.isDbLockedByCurrentThread()) {
            rowId = insertInsideTx(entity, stmt);
        } else {
            // Do TX to acquire a connection before locking the stmt to avoid deadlocks
            //2、如果没有被锁定,为了避免死锁,开启一个事务,再进行插入
            db.beginTransaction();
            try {
                rowId = insertInsideTx(entity, stmt);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
        //3、最后,如果有设置主键,则把数据实体缓存到identityScope中
        if (setKeyAndAttach) {
            updateKeyAfterInsertAndAttach(entity, rowId, true);
        }
        return rowId;
    }
}

上面继续调用了insertInsideTx()方法

  • 同步锁,保证线程安全
  • 如果是标准数据库,使用stmt获取DatabaseStatement,再将实体的字段,绑定到语句中,执行插入SQL语句
  • 如果是加密型数据库,直接把实体的字段,绑定到这个stmt,执行插入SQL语句
public abstract class AbstractDao<T, K> {
    private long insertInsideTx(T entity, DatabaseStatement stmt) {
        //同步锁,保证线程安全
        synchronized (stmt) {
            if (isStandardSQLite) {
                //如果是标准数据库,使用stmt获取DatabaseStatement
                SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
                //再将实体的字段,绑定到语句中
                bindValues(rawStmt, entity);
                //执行插入SQL语句
                return rawStmt.executeInsert();
            } else {
                //如果是加密型数据库,直接把实体的字段,绑定到这个stmt
                bindValues(stmt, entity);
                //执行插入SQL语句
                return stmt.executeInsert();
            }
        }
    }
}

bindValues()方法,是一个抽象方法,在父类AbstractDao上声明,Dao子类进行复写实现,例如这里是PersonInfoEntityDao

public class PersonInfoEntityDao extends AbstractDao<PersonInfoEntity, String> {
    @Override
    protected final void bindValues(DatabaseStatement stmt, PersonInfoEntity entity) {
        stmt.clearBindings();
 
        String id = entity.getId();
        if (id != null) {
            stmt.bindString(1, id);
        }
        stmt.bindLong(2, entity.getCreateTime().getTime());
        stmt.bindLong(3, entity.getUpdateTime().getTime());
        stmt.bindLong(4, entity.getDeleteFlag());
        stmt.bindLong(5, entity.getVersion());
        stmt.bindString(6, entity.getPersonName());
        stmt.bindString(7, entity.getSex());
        stmt.bindLong(8, entity.getAge());
    }

    @Override
    protected final void bindValues(SQLiteStatement stmt, PersonInfoEntity entity) {
        stmt.clearBindings();
 
        String id = entity.getId();
        if (id != null) {
            stmt.bindString(1, id);
        }
        stmt.bindLong(2, entity.getCreateTime().getTime());
        stmt.bindLong(3, entity.getUpdateTime().getTime());
        stmt.bindLong(4, entity.getDeleteFlag());
        stmt.bindLong(5, entity.getVersion());
        stmt.bindString(6, entity.getPersonName());
        stmt.bindString(7, entity.getSex());
        stmt.bindLong(8, entity.getAge());
    }
}

查询流程分析

//获取Dao对象
PersonInfoEntityDao dao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao();
//查
List<PersonInfoEntity> list = dao.loadAll();

loadAll()方法,同样在父类AbstractDao上,和插入一样,通过statements对象,创建查询SQL语句,执行语句,拿到结果Cursor游标,loadAll()方法接着调用loadAllFromCursor()

loadAllFromCursor(),通过游标Cursor来遍历数据

  • 判断游标cursor,是否是一个跨进程的CrossProcessCursor
  • 并且,cursor的行数没有偏差的话,则创建一个加快版的FastCursor进行游标遍历
  • loadCurrent(),遍历游标,并把数据组装到实体类
public abstract class AbstractDao<T, K> {
    protected final TableStatements statements;
    
    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;
    }

    //查询所有
    public List<T> loadAll() {
        Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
        return loadAllAndCloseCursor(cursor);
    }
    
    protected List<T> loadAllAndCloseCursor(Cursor cursor) {
        try {
            return loadAllFromCursor(cursor);
        } finally {
            cursor.close();
        }
    }
    
    protected List<T> loadAllFromCursor(Cursor cursor) {
        int count = cursor.getCount();
        if (count == 0) {
            return new ArrayList<T>();
        }
        List<T> list = new ArrayList<T>(count);
        CursorWindow window = null;
        boolean useFastCursor = false;
        //判断游标cursor,是否是一个跨进程的CrossProcessCursor
        if (cursor instanceof CrossProcessCursor) {
            window = ((CrossProcessCursor) cursor).getWindow();
            if (window != null) { // E.g. Robolectric has no Window at this point
                //并且,cursor的行数没有偏差的话,则创建一个加快版的FastCursor进行游标遍历
                if (window.getNumRows() == count) {
                    cursor = new FastCursor(window);
                    useFastCursor = true;
                } else {
                    DaoLog.d("Window vs. result size: " + window.getNumRows() + "/" + count);
                }
            }
        }

        if (cursor.moveToFirst()) {
            if (identityScope != null) {
                identityScope.lock();
                identityScope.reserveRoom(count);
            }

            try {
                if (!useFastCursor && window != null && identityScope != null) {
                    loadAllUnlockOnWindowBounds(cursor, window, list);
                } else {
                    do {
                        //遍历游标,并把数据组装到实体类
                        list.add(loadCurrent(cursor, 0, false));
                    } while (cursor.moveToNext());
                }
            } finally {
                if (identityScope != null) {
                    identityScope.unlock();
                }
            }
        }
        return list;
    }
}

loadCurrent,把游标的数据组装到对应的实体类中

  • 先从缓存中获取实体数据,根据主键的类型,如果是数字类型,从identityScopeLong缓存中获取,非数字类型,则从identityScope中获取
  • 如果获取到了,直接返回,没有获取到,则创建一个实体
  • 再把实体缓存起来,返回实体
final protected T loadCurrent(Cursor cursor, int offset, boolean lock) {
    //主键为数据类型
    if (identityScopeLong != null) {
        if (offset != 0) {
            // Occurs with deep loads (left outer joins)
            if (cursor.isNull(pkOrdinal + offset)) {
                return null;
            }
        }

        //生成缓存Key
        long key = cursor.getLong(pkOrdinal + offset);
        //先从缓存identityScopeLong中获取数据实体
        T entity = lock ? identityScopeLong.get2(key) : identityScopeLong.get2NoLock(key);
        //如果能获取到缓存实体,直接返回去
        if (entity != null) {
            return entity;
        } else {
            //获取不到,创建一个数据实体
            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);
            //缓存实体到identityScope
            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;
    }
}

readEntity(),通过Cursor游标,取出数据并设置到一个新的实体上,再返回。这个方法在父类AbstractDao中定义,子类中复写进行实现

//Dao父类
public abstract class AbstractDao<T, K> {
    abstract protected T readEntity(Cursor cursor, int offset);
}

//Dao子类
public class PersonInfoEntityDao extends AbstractDao<PersonInfoEntity, String> {
    @Override
    public PersonInfoEntity readEntity(Cursor cursor, int offset) {
        PersonInfoEntity entity = new PersonInfoEntity( //
            cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // id
            new java.util.Date(cursor.getLong(offset + 1)), // createTime
            new java.util.Date(cursor.getLong(offset + 2)), // updateTime
            cursor.getInt(offset + 3), // deleteFlag
            cursor.getInt(offset + 4), // version
            cursor.getString(offset + 5), // personName
            cursor.getString(offset + 6), // sex
            cursor.getInt(offset + 7) // age
        );
        return entity;
    }
}

由于GreenDao在CRUD时,会缓存实体数据,所以如果通过其他数据库框架,或者手写SQL对表进行修改,则会导致GreenDao有脏数据,这时通过DaoSessionclear()方法,清空缓存即可

public class DaoSession extends AbstractDaoSession {
    //...
    
    public void clear() {
        personInfoEntityDaoConfig.clearIdentityScope();
    }
    
    //...
}

GreenDao和RxJava1结合

GreenDao提供了与RxJava1结合的操作方法,来看看怎么使用

//获取Dao对象
RxDao<PersonInfoEntity, String> rxDao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao().rx();

//增
dao.insert(entity)
       .observerOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<PersonInfoEntity>() {
            @Override
            public void call(PersonInfoEntity entity) {
               //插入成功
            }
        });

AbstractDao,rx()创建RxDao对象,传入了Schedulers.io()IO调度器

  • 通过wrap()方法,调用fromCallable(),把Callable回调包装成Observable返回
  • 如果RxDao创建时,有调度器,则进行线程切换,在订阅时在指定的线程中执行,例如 Schedulers.io(),就在IO线程
  • Callable回调中,依旧是使用Dao层的insert方法进行插入
  • Observable.defer()defer()操作符,是确保Observable被订阅后,才执行Callable回调,进行数据库操作
public abstract class AbstractDao<T, K> {
    //不指定调度器,需要自己指定`subscribeOn`操作符在哪个调度器中执行数据库操作
    @Experimental
    public RxDao<T, K> rxPlain() {
        if (rxDaoPlain == null) {
            rxDaoPlain = new RxDao<>(this);
        }
        return rxDaoPlain;
    }

    @Experimental
    public RxDao<T, K> rx() {
        //指定调度器为Schedulers.io()
        if (rxDao == null) {
            rxDao = new RxDao<>(this, Schedulers.io());
        }
        return rxDao;
    }
}

@Experimental
public class RxDao<T, K> extends RxBase {
    //插入
    @Experimental
    public Observable<T> insert(final T entity) {
        return wrap(new Callable<T>() {
            @Override
            public T call() throws Exception {
                //调用Dao的插入
                dao.insert(entity);
                return entity;
            }
        });
    }
    
    //包装Callable为Observable
    protected <R> Observable<R> wrap(Callable<R> callable) {
        return wrap(RxUtils.fromCallable(callable));
    }
    
    protected <R> Observable<R> wrap(Observable<R> observable) {
        //如果RxDao创建时,有调度器,则进行线程切换,在订阅时在指定的线程中执行,例如 Schedulers.io(),就在IO线程
        if (scheduler != null) {
            return observable.subscribeOn(scheduler);
        } else {
            return observable;
        }
    }
}

@Internal
class RxUtils {
    /** As of RxJava 1.1.7, Observable.fromCallable is still @Beta, so just in case... */
    @Internal
    static <T> Observable<T> fromCallable(final Callable<T> callable) {
        //defer()操作符,是确保Observable被订阅后,才执行Callable回调,进行数据库操作
        return Observable.defer(new Func0<Observable<T>>() {
            @Override
            public Observable<T> call() {
                T result;
                try {
                    result = callable.call();
                } catch (Exception e) {
                    return Observable.error(e);
                }
                return Observable.just(result);
            }
        });
    }
}

相关文章

网友评论

    本文标题:GreenDao源码分析

    本文链接:https://www.haomeiwen.com/subject/qokgertx.html