ORM对象关系映射之GreenDAO源码解析

时间:2022-06-08 06:17:19

上一篇我们学习了GreenDAO的CRUD基本操作,可以说是非常的方便的,而且GreenDAO的效率和性能远远高于其它两款流行的ORM框架,下面是我从官网找的一副它们三个ORM框架之间的性能测试的直观图:

ORM对象关系映射之GreenDAO源码解析

那么这篇我们就从源码的角度来学习GreenDAO更深层次的工作原理。

GreenDAO的工作原理图解:

ORM对象关系映射之GreenDAO源码解析

从上篇我们知道,如果Android项目中要使用GreenDAO框架,需要先创建一个Java项目用于生成实体类和DAO类,然后在Android项目中使用这些类,在此过程中分别需要对Android项目添加GreenDAO的核心包依赖和对Java项目添加generator包依赖,所以解析GreenDAO的源码需要解析两部分,而这里只解析GreenDAO核心包在Android项目中的工作原理,generator包中的原理很简单,总的来说有四个作用:就是用于生成实体类、DAO类、建立多表之间的关联以及配置实体类的接口和序列化功能(后面的两个功能后续会讲到)。

而在Android项目中用到的最核心的四个类就是:DaoMaster、DaoSession、实体类、实体Dao类。当然还有与CRUD操作密切联系的Property类(Android项目中的核心包和Java项目中的generator包分别都有这个类,不过这两个类作用分别不同,核心包中的主要是针对实体类的属性的,而generator包中的主要是针对表中的字段的)、QueryBuilder类,也至关重要,不过这两个类相信看下源码就懂了,就不多叙述了,而主要讲解这四个核心类。

四个核心类:

这四个核心类的功能体系如下图所示:

ORM对象关系映射之GreenDAO源码解析

DaoMaster

我们知道在使用GreenDAO时候,我们的入口点就是通过DaoMaster的静态内部类DevOpenHelper来创建一个DevOpenHelper对象

DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "students-db", null);

而DevOpenHelper对象又是什么呢?我们都知道在使用SQLite的时候,我们必须通过继承SQLiteOpenHelper类并且实现它内部的一些方法来创建数据库,而这里仅仅通过DevOpenHelper类就成功创建了一个文件名为”students-db”的数据表,那么内部又是怎么实现的呢?我们可以看看DaoMaster类的源码(以上一篇生成的为例讲):

public class DaoMaster extends AbstractDaoMaster {
    public static final int SCHEMA_VERSION = 1;

    /** Creates underlying database table using DAOs. */
    public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) {
        StudentDao.createTable(db, ifNotExists);
    }

    /** Drops underlying database table using DAOs. */
    public static void dropAllTables(SQLiteDatabase db, boolean ifExists) {
        StudentDao.dropTable(db, ifExists);
    }

    public static abstract class OpenHelper extends SQLiteOpenHelper {

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

        @Override
        public void onCreate(SQLiteDatabase 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, CursorFactory factory) {
            super(context, name, factory);
        }

        @Override
        public void onUpgrade(SQLiteDatabase 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 DaoMaster(SQLiteDatabase db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(StudentDao.class);
    }

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

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

}

从DaoMaster中我们可以发现,DaoMaster除了具有创建表和删除表的两个功能外,还有两个内部类,分别为OpenHelper和DevOpenHelper,而DevOpenHelper继承自OpenHelper,而OpenHelper继承自SQLiteOpenHelper,而重写的onCreate()方法中调用了createAllTables(db,false);方法来创建数据表,而createAllTables()方法中是通过调用StudentDao静态方法来创建表的StudentDao.createTable(db, ifNotExists);我们点进这个方法中去看个究竟:

public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"STUDENT\" (" + //
                "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
                "\"NAME\" TEXT NOT NULL ," + // 1: name
                "\"AGE\" INTEGER," + // 2: age
                "\"IS_MAN\" INTEGER);"); // 3: is_man
    }

发现它内部就是通过sql语句来创建表的,只不过GreenDAO帮我们封装好了,而且你会发现删除表其实也一样:

public static void dropTable(SQLiteDatabase db, boolean ifExists) {
        String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"STUDENT\"";
        db.execSQL(sql);
    }

好了,现在我们知道了通过DevOpenHelper是怎么创建表的,而细心的同学会发现在DevOpenHelper类中实现了onUpgrade()方法,就是更新数据库的方法,它在更新数据表的时候会把以前的数据表删除后再重新创建,所以这个你必须注意,当我们在利用GreenDAO更新数据表的时候,如果你想以前表中的数据保存下来的话,我们必须自己封装一个方法。接下来就是newSession()方法了,这个当然就是得到DaoSession实例了,关于DaoSession实例,GreenDAO官方建议不要重新创建新的实例,保持一个单例的引用即可。好了,DaoMaster源码看完了,接下来就是看它的父类AbstractDaoMaster的源码了,它的源码如下:

public abstract class AbstractDaoMaster {
    protected final SQLiteDatabase db;
    protected final int schemaVersion;
    protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;

    public AbstractDaoMaster(SQLiteDatabase 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);
    }

    public int getSchemaVersion() {
        return schemaVersion;
    }

    /** Gets the SQLiteDatabase for custom database access. Not needed for greenDAO entities. */
    public SQLiteDatabase getDatabase() {
        return db;
    }

    public abstract AbstractDaoSession newSession();

    public abstract AbstractDaoSession newSession(IdentityScopeType type);
}

看这个类的代码,其中最让我们受关注的无非就是这一行了

protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;

这里定义了一个Map集合,Key是继承自AbstractDao类的字节码对象,Value则为DaoConfig对象,而往这个Map集合中put数据是通过这个方法registerDaoClass()

protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        daoConfigMap.put(daoClass, daoConfig);
    }

所以Map的功能现在很清楚了,就是为每一个EntityDao字节码对象建立与之对应的db数据库的映射关系,从而管理所有的EntityDao类。而这个方法在哪里调用了呢?我们回到DaoMaster的源码中,发现在DaoMaster类的构造方法中调用了,并且传入了Student.class,所以我们在创建DaoMaster对象的时候也同时为EntityDao类和相应的数据库db建立好了关联。

DaoSession

从上面可知DaoSession对象是通过master.newSession();创建的。我们看看DaoSession源码,发现它也有一个抽象的父类AbstractDaoSession,我们来看看DaoSession的源码:

public class DaoSession extends AbstractDaoSession {

    private final DaoConfig studentDaoConfig;

    private final StudentDao studentDao;

    public DaoSession(SQLiteDatabase db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        super(db);

        studentDaoConfig = daoConfigMap.get(StudentDao.class).clone();
        studentDaoConfig.initIdentityScope(type);

        studentDao = new StudentDao(studentDaoConfig, this);

        registerDao(Student.class, studentDao);
    }

    public void clear() {
        studentDaoConfig.getIdentityScope().clear();
    }

    public StudentDao getStudentDao() {
        return studentDao;
    }

}

就那么几行,其中最主要的一个方法就是通过getStudentDao()来得到StudentDao实例,而创建一个StudentDao对象正是在DaoSession的构造方法中,其中有这么一行:

studentDaoConfig = daoConfigMap.get(StudentDao.class).clone();

这个正是从在DaoMaster创建的Map集合中取出keyStudentDao.class的DaoConfig对象,刚刚就说了Map集合中保寸了StudentDao类对应的数据库db的关系映射,而这个DaoConfig对象正是管理了对应的db对象。然后把这个DaoConfig传给StudentDao(studentDaoConfig, this),所以这就说明了我们使用StudentDao对象来进行数据库上的CRUD操作而对应的数据库也会变化的原因,这个过程实际上就是在间接操作数据库。

好了,接下来就是看看它的父类了:

这个类比较长,所以我删除了几个方法,这不影响理解的

public class AbstractDaoSession {
    private final SQLiteDatabase db;
    private final Map<Class<?>, AbstractDao<?, ?>> entityToDao;

    public AbstractDaoSession(SQLiteDatabase db) {
        this.db = db;
        this.entityToDao = new HashMap<Class<?>, AbstractDao<?, ?>>();
    }

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

    /** Convenient call for {@link AbstractDao#insert(Object)}. */
    public <T> long insert(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        return dao.insert(entity);
    }

    /** Convenient call for {@link AbstractDao#insertOrReplace(Object)}. */
    public <T> long insertOrReplace(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        return dao.insertOrReplace(entity);
    }

    /** Convenient call for {@link AbstractDao#refresh(Object)}. */
    public <T> void refresh(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.refresh(entity);
    }

    /** Convenient call for {@link AbstractDao#update(Object)}. */
    public <T> void update(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.update(entity);
    }
//...
    /** Convenient call for {@link AbstractDao#delete(Object)}. */
    public <T> void delete(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.delete(entity);
    }

    /** Convenient call for {@link AbstractDao#deleteAll()}. */
    public <T> void deleteAll(Class<T> entityClass) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entityClass);
        dao.deleteAll();
    }

    /** Convenient call for {@link AbstractDao#load(Object)}. */
    public <T, K> T load(Class<T> entityClass, K key) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
        return dao.load(key);
    }

    /** Gets the SQLiteDatabase for custom database access. Not needed for greenDAO entities. */
    public SQLiteDatabase getDatabase() {
        return db;
    }

    /**
     * Creates a new {@link AsyncSession} to issue asynchronous entity operations. See {@link AsyncSession} for details.
     */
    public AsyncSession startAsyncSession() {
        return new AsyncSession(this);
    }

}

可以看到它的父类中,大部分方法都是进行CRUD操作的,而事实上我们在进行CRUD操作都是通过StudentDao对象来进行的,实际上这两种做法没有区别,因为它内部本身就是通过dao对象来进行CRUD操作的,大家看看这些方法的返回值就知道了。

到此,我们只看到了DaoSession源码表面上的功能,这些功能就是它管理了指定模式下所有可用的DAO对象,并且提供了getter方法供我们得到这些DAO对象,它还提供了一些CRUD方法。实际上DaoSession和StudentDao在调用CRUD的方法进行CRUD操作时,其中的查询操作就是最特别的,为什么呢?原因是GreenDao在查询这块加了缓存,有趣吧,GreenDao在查询时使用了弱引用WeakReference,假如第一次查询时候我查询了小明这个Student的数据,那么它将把小明加入一个SparseArray<WeakReference<Q>>的集合中,下次如果再次查询小明这个学生的时候,将立即会返回这个引用从而不必再查询数据库(前提是GC还没回收这些引用)。

这个缓存的代码是在AbstractQueryData类中,如下:

SparseArray<WeakReference<Q>> queriesForThreads = new SparseArray<WeakReference<Q>>();
//...
Q forCurrentThread() {
        int threadId = Process.myTid();
        synchronized (queriesForThreads) {
            WeakReference<Q> queryRef = queriesForThreads.get(threadId);
            Q query = queryRef != null ? queryRef.get() : null;
            if (query == null) {
                gc();
                query = createQuery();
                queriesForThreads.put(threadId, new WeakReference<Q>(query));
            } else {
                System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length);
            }
            return query;
        }
    }

Entity(实体类)

对于实体类,这没什么可讲的,就是一个Bean,一个实体类对应一张表,实体类里面有对应各个字段的getter和setter方法

EntityDao(实体Dao类)

同样,它也有一个抽象的父类AbstractDao,我们先看看StudentDao类的源码:

public class StudentDao extends AbstractDao<Student, Long> {

    public static final String TABLENAME = "STUDENT";

    /**
     * Properties of entity Student.<br/>
     * Can be used for QueryBuilder and for referencing column names.
    */
    public static class Properties {
        public final static Property Id = new Property(0, Long.class, "id", true, "_id");
        public final static Property Name = new Property(1, String.class, "name", false, "NAME");
        public final static Property Age = new Property(2, Integer.class, "age", false, "AGE");
        public final static Property Is_man = new Property(3, Boolean.class, "is_man", false, "IS_MAN");
    };

    public StudentDao(DaoConfig config) {
        super(config);
    }

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

    /** Creates the underlying database table. */
    public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"STUDENT\" (" + //
                "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
                "\"NAME\" TEXT NOT NULL ," + // 1: name
                "\"AGE\" INTEGER," + // 2: age
                "\"IS_MAN\" INTEGER);"); // 3: is_man
    }

    /** Drops the underlying database table. */
    public static void dropTable(SQLiteDatabase db, boolean ifExists) {
        String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"STUDENT\"";
        db.execSQL(sql);
    }

    /** @inheritdoc */
    @Override
    protected void bindValues(SQLiteStatement stmt, Student entity) {
        stmt.clearBindings();

        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
        stmt.bindString(2, entity.getName());

        Integer age = entity.getAge();
        if (age != null) {
            stmt.bindLong(3, age);
        }

        Boolean is_man = entity.getIs_man();
        if (is_man != null) {
            stmt.bindLong(4, is_man ? 1L: 0L);
        }
    }

    /** @inheritdoc */
    @Override
    public Long readKey(Cursor cursor, int offset) {
        return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
    }    

    /** @inheritdoc */
    @Override
    public Student readEntity(Cursor cursor, int offset) {
        Student entity = new Student( //
            cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
            cursor.getString(offset + 1), // name
            cursor.isNull(offset + 2) ? null : cursor.getInt(offset + 2), // age
            cursor.isNull(offset + 3) ? null : cursor.getShort(offset + 3) != 0 // is_man
        );
        return entity;
    }

    /** @inheritdoc */
    @Override
    public void readEntity(Cursor cursor, Student entity, int offset) {
        entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
        entity.setName(cursor.getString(offset + 1));
        entity.setAge(cursor.isNull(offset + 2) ? null : cursor.getInt(offset + 2));
        entity.setIs_man(cursor.isNull(offset + 3) ? null : cursor.getShort(offset + 3) != 0);
     }

    /** @inheritdoc */
    @Override
    protected Long updateKeyAfterInsert(Student entity, long rowId) {
        entity.setId(rowId);
        return rowId;
    }

    /** @inheritdoc */
    @Override
    public Long getKey(Student entity) {
        if(entity != null) {
            return entity.getId();
        } else {
            return null;
        }
    }

    /** @inheritdoc */
    @Override
    protected boolean isEntityUpdateable() {
        return true;
    }

}

其中bindValues()这个方法就是绑定实体的属性名和表中的字段名的,还有比较重要的就是这个静态内部类Properties,该类中分别对每一个实体类的属性都创建了一个Property对象,而我们可以根据Property来得到这个属性对应表中的列名、是否为主键等值,其中还包括了一些方法,比如判断表中某个字段的值是否和value相等:eq(Object value);

Property源码如下:

public class Property {
    public final int ordinal;
    public final Class<?> type;
    public final String name;
    public final boolean primaryKey;
    public final String columnName;

    public Property(int ordinal, Class<?> type, String name, boolean primaryKey, String columnName) {
        this.ordinal = ordinal;
        this.type = type;
        this.name = name;
        this.primaryKey = primaryKey;
        this.columnName = columnName;
    }

    /** Creates an "equal ('=')" condition  for this property. */
    public WhereCondition eq(Object value) {
        return new PropertyCondition(this, "=?", value);
    }

    /** Creates an "not equal ('<>')" condition  for this property. */
    public WhereCondition notEq(Object value) {
        return new PropertyCondition(this, "<>?", value);
    }

    /** Creates an "LIKE" condition  for this property. */
    public WhereCondition like(String value) {
        return new PropertyCondition(this, " LIKE ?", value);
    }

    /** Creates an "BETWEEN ... AND ..." condition  for this property. */
    public WhereCondition between(Object value1, Object value2) {
        Object[] values = { value1, value2 };
        return new PropertyCondition(this, " BETWEEN ? AND ?", values);
    }

    /** Creates an "IN (..., ..., ...)" condition  for this property. */
    public WhereCondition in(Object... inValues) {
        StringBuilder condition = new StringBuilder(" IN (");
        SqlUtils.appendPlaceholders(condition, inValues.length).append(')');
        return new PropertyCondition(this, condition.toString(), inValues);
    }

    /** Creates an "IN (..., ..., ...)" condition  for this property. */
    public WhereCondition in(Collection<?> inValues) {
        return in(inValues.toArray());
    }

    /** Creates an "NOT IN (..., ..., ...)" condition  for this property. */
    public WhereCondition notIn(Object... notInValues) {
        StringBuilder condition = new StringBuilder(" NOT IN (");
        SqlUtils.appendPlaceholders(condition, notInValues.length).append(')');
        return new PropertyCondition(this, condition.toString(), notInValues);
    }

    /** Creates an "NOT IN (..., ..., ...)" condition  for this property. */
    public WhereCondition notIn(Collection<?> notInValues) {
        return notIn(notInValues.toArray());
    }

    /** Creates an "greater than ('>')" condition  for this property. */
    public WhereCondition gt(Object value) {
        return new PropertyCondition(this, ">?", value);
    }

    /** Creates an "less than ('<')" condition  for this property. */
    public WhereCondition lt(Object value) {
        return new PropertyCondition(this, "<?", value);
    }

    /** Creates an "greater or equal ('>=')" condition  for this property. */
    public WhereCondition ge(Object value) {
        return new PropertyCondition(this, ">=?", value);
    }

    /** Creates an "less or equal ('<=')" condition  for this property. */
    public WhereCondition le(Object value) {
        return new PropertyCondition(this, "<=?", value);
    }

    /** Creates an "IS NULL" condition  for this property. */
    public WhereCondition isNull() {
        return new PropertyCondition(this, " IS NULL");
    }

    /** Creates an "IS NOT NULL" condition  for this property. */
    public WhereCondition isNotNull() {
        return new PropertyCondition(this, " IS NOT NULL");
    }

}

而AbstractDao源码中主要是一些CRUD方法和其它的一些方法:

源码太长了,避免影响篇幅,我删了一部分

public abstract class AbstractDao<T, K> {
    protected final SQLiteDatabase db;
    protected final DaoConfig config;
    protected IdentityScope<K, T> identityScope;
    protected IdentityScopeLong<T> identityScopeLong;
    protected TableStatements statements;

    protected final AbstractDaoSession session;
    protected final int pkOrdinal;

    public AbstractDao(DaoConfig config) {
        this(config, null);
    }

    @SuppressWarnings("unchecked")
    public AbstractDao(DaoConfig config, AbstractDaoSession daoSession) {
        this.config = config;
        this.session = daoSession;
        db = config.db;
        identityScope = (IdentityScope<K, T>) config.getIdentityScope();
        if (identityScope instanceof IdentityScopeLong) {
            identityScopeLong = (IdentityScopeLong<T>) identityScope;
        }
        statements = config.statements;
        pkOrdinal = config.pkProperty != null ? config.pkProperty.ordinal : -1;
    }

    public AbstractDaoSession getSession() {
        return session;
    }

    TableStatements getStatements() {
        return config.statements;
    }

    public String getTablename() {
        return config.tablename;
    }

    public Property[] getProperties() {
        return config.properties;
    }

    public Property getPkProperty() {
        return config.pkProperty;
    }

    public String[] getAllColumns() {
        return config.allColumns;
    }

    public String[] getPkColumns() {
        return config.pkColumns;
    }

    public String[] getNonPkColumns() {
        return config.nonPkColumns;
    }
    //...

    /**
     * Deletes the given entities in the database using a transaction.
     *
     * @param entities
     *            The entities to delete.
     */
    public void deleteInTx(Iterable<T> entities) {
        deleteInTxInternal(entities, null);
    }

    /**
     * Deletes the given entities in the database using a transaction.
     *
     * @param entities
     *            The entities to delete.
     */
    public void deleteInTx(T... entities) {
        deleteInTxInternal(Arrays.asList(entities), null);
    }

    /**
     * Deletes all entities with the given keys in the database using a transaction.
     *
     * @param keys
     *            Keys of the entities to delete.
     */
    public void deleteByKeyInTx(Iterable<K> keys) {
        deleteInTxInternal(null, keys);
    }

    /**
     * Deletes all entities with the given keys in the database using a transaction.
     *
     * @param keys
     *            Keys of the entities to delete.
     */
    public void deleteByKeyInTx(K... keys) {
        deleteInTxInternal(null, Arrays.asList(keys));
    }

    /** Resets all locally changed properties of the entity by reloading the values from the database. */
    public void refresh(T entity) {
        assertSinglePk();
        K key = getKeyVerified(entity);
        String sql = statements.getSelectByKey();
        String[] keyArray = new String[] { key.toString() };
        Cursor cursor = db.rawQuery(sql, keyArray);
        try {
            boolean available = cursor.moveToFirst();
            if (!available) {
                throw new DaoException("Entity does not exist in the database anymore: " + entity.getClass()
                        + " with key " + key);
            } else if (!cursor.isLast()) {
                throw new DaoException("Expected unique result, but count was " + cursor.getCount());
            }
            readEntity(cursor, entity, 0);
            attachEntity(key, entity, true);
        } finally {
            cursor.close();
        }
    }

    public void update(T entity) {
        assertSinglePk();
        SQLiteStatement stmt = statements.getUpdateStatement();
        if (db.isDbLockedByCurrentThread()) {
            synchronized (stmt) {
                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();
            }
        }
    }

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

好了,核心类已经介绍完了,下面梳理一下它们主要做了什么事。

GreenDAO工作原理

我们首先来看看使用GreenDAO的基本步骤:

//生成数据库文件,名为students-db
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "students-db", null);
SQLiteDatabase db = helper.getWritableDatabase();
//建立特定模式下的所有的DAO对象和数据库db对象的映射
DaoMaster master = new DaoMaster(db);
//管理特定模式下的所有DAO对象,并提供一些通用的CRUD持久化方法
DaoSession session = master.newSession();
//得到指定的StudentDao对象
StudentDao dao = session.getStudentDao();
dao.insert(student);
//...