美文网首页
GreenDao 源码剖析

GreenDao 源码剖析

作者: 01_小小鱼_01 | 来源:发表于2018-04-12 00:01 被阅读74次

    上一篇文章,我们有讲解到GreenDao的基本应用,本文开始深了解GreenDao的源码。当我们编写好了Entity类之后,会自动生成DaoMaster、DaoSession、Entity Dao这三个核心类。
    三者之间的关系如下:

    //生成数据库文件,名为students-db
    DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "xxx.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(entity);
    //...
    
    1. DaoMaster
    public class DaoMaster extends AbstractDaoMaster {
        public static final int SCHEMA_VERSION = 1;
        // 创建数据库
        public static void createAllTables(Database db, boolean ifNotExists) {
            UserDao.createTable(db, ifNotExists);
        }
        // 删除数据库
        public static void dropAllTables(Database db, boolean ifExists) {
            UserDao.dropTable(db, ifExists);
        }
        // 获取DaoSession对象
        public static DaoSession newDevSession(Context context, String name) {
            Database db = new DevOpenHelper(context, name).getWritableDb();
            DaoMaster daoMaster = new DaoMaster(db);
            return daoMaster.newSession();
        }
    
        public DaoMaster(SQLiteDatabase db) {
            this(new StandardDatabase(db));
        }
    
        public DaoMaster(Database db) {
            super(db, SCHEMA_VERSION);
            //注意此处传入UserDao对象
            registerDaoClass(UserDao.class);
        }
    
        public DaoSession newSession() {
            return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
        }
    
        public DaoSession newSession(IdentityScopeType type) {
            return new DaoSession(db, type, daoConfigMap);
        }
        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);
            }
        }
    
        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);
            }
        }
    }
    

    DaoMaster类里面有两个静态内部类OpenHelper继承了DatabaseOpenHelper类,而DatabaseOpenHelper类又继承了SQLiteOpenHelper类。DevOpenHelper类则继承了OpenHelper。DevOpenHelper类的构造函数里面参数name代表类数据库名称。onCreate函数会调用createAllTables函数,onUpgrade函数会调用dropAllTables,再重新创建数据库。

    AbstractDaoMaster.java

    public abstract class AbstractDaoMaster {
        protected final SQLiteDatabase db;
        protected final int schemaVersion;
        // EntityDao类为Key
        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>();
        }
    
        // 初始化daoConfigMap对象
        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);
    }
    

    daoConfigMap其实是存储了EntityDao与db数据库之前的映射关系。

    2. 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;
        }
    }
    

    AbstractDaoSession.java

    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);
        }
    }
    
    3. Dao
    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");
        }
    }
    

    更多内容

    1. GreenDao源码分析及使用GreenDao实现静态数据缓存
    2. Android数据存储之GreenDao 3.0 详解
    3. Android | 分析greenDAO 3.2实现原理
    4. ORM对象关系映射之GreenDAO源码解析

    相关文章

      网友评论

          本文标题:GreenDao 源码剖析

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