GreenDao详解二

在上一篇GreenDao详解一中有讲到,GreenDaobuild的时候,会自动生成DaoMaster, DaoSession,xxDao三个文件,我们通过上一篇的知识了解到,插入一条数据可以这样写:

private void insertUser(User user){
      //DaoMaster daoMaster = DbManager.getDaoMaster(mContext);
        DaoMaster.DevOpenHelper mDevOpenHelper = new DaoMaster.DevOpenHelper(mContext, DB_NAME);
        DaoMaster mDaoMaster = new DaoMaster(mDevOpenHelper.getWritableDb());
        UserDao userDao = daoMaster.newSession().getUserDao();
        userDao.insert(user);
}

先来理一理他们的调用关系:
DaoMaster中创建并获得DaoSession,而DaoSession创建和管理xxDao,而xxDao则对xxEntity进行数据的CRUD操作。为了直观,这里给出了一个关系图:

GreenDao_Generator.png

DaoMaster

DaoMaster是对数据库进行相关操作的一个封装类,内部包括了创建表,删除表的方法,还包括两个静态抽象类OpenHelperDevOpenHelperOpenHelper继承于SQLiteOpenHelper,通过源码我们看到最终会调用到createAllTabes方法来创建数据库中的表;而DevOpenHelper继承于OpenHelper,主要通过onUpgrade方法用于数据库的升级,另外,它还包含一个newSession()方法,在方法内返回一个DaoSession对象,DaoSession是连接GreenDao框架到SQLite数据库的桥梁,通过该对象我们可以得到一个与xxx表相关的操作对象xxxDao
我们先来看看DaoMaster的源码:

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) {
        UserDao.createTable(db, ifNotExists);
    }

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

    /**
     * WARNING: Drops all table on Upgrade! Use only during development.
     * Convenience method using a {@link DevOpenHelper}.
     */
    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);
        registerDaoClass(UserDao.class);
    }

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

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

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

在DaoMaster构造函数中,会调用父类AbstractDaoMaster的构造函数和registerDaoClass方法:

 public DaoMaster(Database db) {
      super(db, SCHEMA_VERSION);
      registerDaoClass(UserDao.class);
 }
 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);
   }

上面的代码中维护了一个daoConfigMap的集合,它是以daoClass为key,DaoConfig为Value的集合。
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;

    /** Single property PK or null if there's no PK or a multi property PK. */
    public final Property pkProperty;
    public final boolean keyIsNumeric;
    public final TableStatements statements;

    private IdentityScope<?, ?> identityScope;

    public DaoConfig(Database db, Class<? extends AbstractDao<?, ?>> daoClass) {
        this.db = db;
        try {
            this.tablename = (String) daoClass.getField("TABLENAME").get(null);
            Property[] properties = reflectProperties(daoClass);
            //省略部分代码
            //pkColumns对应表中主键的集合,allColumns表中所有字段集合
            statements = new TableStatements(db, tablename, allColumns, pkColumns);
            //省略部分代码
        } catch (Exception e) {
            throw new DaoException("Could not init DAOConfig", e);
        }
    }

    private static Property[] reflectProperties(Class<? extends AbstractDao<?, ?>> daoClass)
            throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
        Class<?> propertiesClass = Class.forName(daoClass.getName() + "$Properties");
        Field[] fields = propertiesClass.getDeclaredFields();

        ArrayList<Property> propertyList = new ArrayList<Property>();
        final int modifierMask = Modifier.STATIC | Modifier.PUBLIC;
        for (Field field : fields) {
            // There might be other fields introduced by some tools, just ignore them (see issue #28)
            if ((field.getModifiers() & modifierMask) == modifierMask) {
                Object fieldValue = field.get(null);
                if (fieldValue instanceof Property) {
                    propertyList.add((Property) fieldValue);
                }
            }
        }

        Property[] properties = new Property[propertyList.size()];
        for (Property property : propertyList) {
            if (properties[property.ordinal] != null) {
                throw new DaoException("Duplicate property ordinals");
            }
            properties[property.ordinal] = property;
        }
        return properties;
    }
  //  //省略部分代码
}

DaoConfig主要通过传入的daoClass参数并通过反射去拿到对应表中的相应信息,并通过TableStatements(db, tablename, allColumns, pkColumns);创建一个TableStatements对象。而这个TableStatements对象注释上面有写是为特定表创建SQL语句的Help类,而该对象会在AbstractDao类中使用,以方便继承了AstractDao的类使用。

DaoSession

DaoSession用于获得能够操作数据库的XXDao对象,它建立了与SQLite数据库之间的连接(会话),其中,getUserDao方法用于返回相UserDao的对象,我们在操作数据库的时候,通过XXDao对象去对数据库数据进行CRUD操作。

public class DaoSession extends AbstractDaoSession {
    private final DaoConfig userDaoConfig;
    private final UserDao userDao;
    public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        super(db);
        userDaoConfig = daoConfigMap.get(UserDao.class).clone();
        userDaoConfig.initIdentityScope(type);
        userDao = new UserDao(userDaoConfig, this);
        registerDao(User.class, userDao);
    }
    
    public void clear() {
        userDaoConfig.clearIdentityScope();
    }

    public UserDao getUserDao() {
        return userDao;
    }
}

DaoSession 继承于AbstractDaoSession,而AbstractDaoSession里面实现了很多操作数据库表的方法,而这些方法最后会调用到AbstractDao中的相关方法,现在我们来看看AbstractDaoSession的实现:

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

    private volatile RxTransaction rxTxPlain;
    private volatile RxTransaction rxTxIo;

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

    /** 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);
    }
    //省略部分代码
}

XXDao

XXDao通过GreenDao生成器生成的数据库操作类,它继承于AbstractDao,从而可以调用AbstractDao中对数据库表进行CRUD的方法。

public class UserDao extends AbstractDao<User, Long> {

    public static final String TABLENAME = "USER";

    /**
     * Properties of entity User.<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 Grade = new Property(2, int.class, "grade", false, "GRADE");
        public final static Property Age = new Property(3, Integer.class, "age", false, "AGE");
    }


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

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

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

    @Override
    protected final void bindValues(DatabaseStatement stmt, User entity) {
        stmt.clearBindings();
 
        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
 
        String name = entity.getName();
        if (name != null) {
            stmt.bindString(2, name);
        }
        stmt.bindLong(3, entity.getGrade());
 
        Integer age = entity.getAge();
        if (age != null) {
            stmt.bindLong(4, age);
        }
    }

    @Override
    protected final void bindValues(SQLiteStatement stmt, User entity) {
        stmt.clearBindings();
 
        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
 
        String name = entity.getName();
        if (name != null) {
            stmt.bindString(2, name);
        }
        stmt.bindLong(3, entity.getGrade());
 
        Integer age = entity.getAge();
        if (age != null) {
            stmt.bindLong(4, age);
        }
    }
  //省略部分代码
}

xxDao里面提供了创建数据表的createTable方法,提供了dropTable方法,使用bindValues进行绑定。
现在我们再来看看AbstractDao的代码:

public abstract class AbstractDao<T, K> {
    //省略部分代码
    public long insert(T entity) {
        return executeInsert(entity, statements.getInsertStatement(), true);
    }

    private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
        long rowId;
        if (db.isDbLockedByCurrentThread()) {
            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;
    }

     private long insertInsideTx(T entity, DatabaseStatement stmt) {
        synchronized (stmt) {
            if (isStandardSQLite) {
                SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
                bindValues(rawStmt, entity);
                return rawStmt.executeInsert();
            } else {
                bindValues(stmt, entity);
                return stmt.executeInsert();
            }
        }
    }
  //省略部分代码
}

我们看到AbstractDao类中定义了很多操作数据表的方法,在这里我们截取了insert方法,我们看到外部调用的insert方法实际调用的是executeInsert,而这个方法中还会继续调用insertInsideTx方法,方法内通过isStandardSQLite变量去判断是调用sqlite中的executeInsert还是greenDao中的executeInsert。走到这里基本就操作数据表中简单的看了一遍。

GreenDao的对数据表的CRUD操作GreenDao详解一已经简单说明了,在这里就不一一重复了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,913评论 2 89
  • GreenDao 介绍:greenDAO是一个对象关系映射(ORM)的框架,能够提供一个接口通过操作对象的方式去操...
    小董666阅读 730评论 0 1
  • (一)GreenDao简介 GreenDao是一个对象关系映射(ORM)的开源框架,目前最主流的安卓数据库操作框架...
    miss2008阅读 5,236评论 4 18
  • 序言 (写分享前的序言)大家好.很高兴能为大家分享Android之GreenDao笔记,也希望能和大家一起学习进步...
    王黎聪阅读 1,088评论 4 11
  • 生来彷徨与孤单。一见钟情不过见色起意,日久生情不过权衡利弊
    春暖花开_jing阅读 88评论 0 0