多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
本篇是greenDao的最后一篇,这一篇带大家看下greenDao的源码。 - dao的初始化过程 这一过程非常的复杂,容易绕晕,那么我就来带大家梳理一下。首先看看我们初始化dao的方法。 ~~~ DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this,"persons-db",null); db = helper.getWritableDatabase(); Log.e("tag","this is db version ->"+db.getVersion()); // 该数据库连接属于DaoMaster,所以多个Session指的是想用的数据库连接 daoMaster = new DaoMaster(db); daoSession =daoMaster.newSession(); return daoSession.getPersonDao(); ~~~ 我在这里返回的是PersionDao。首先看下helper的初始化 过程。 ~~~ public DevOpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory); } ~~~ 调用父类的构造方法。 ~~~ 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); } } ~~~ 在父类中完成数据表的创建。 ~~~ public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) { PersonDao.createTable(db, ifNotExists); PeopleDao.createTable(db, ifNotExists); IdCardDao.createTable(db, ifNotExists); OrderDao.createTable(db, ifNotExists); CourseDao.createTable(db, ifNotExists); } ~~~ ~~~ public static void createTable(SQLiteDatabase db, boolean ifNotExists) { String constraint = ifNotExists? "IF NOT EXISTS ": ""; db.execSQL("CREATE TABLE " + constraint + "\"PERSON\" (" + // "\"_id\" INTEGER PRIMARY KEY ," + // 0: id "\"NAME\" TEXT NOT NULL ," + // 1: name "\"AGE\" INTEGER NOT NULL ," + // 2: age "\"CARD\" TEXT);"); // 3: card } ~~~ 这么一来,表的创建过程就理清楚了。接下来看DaoMaster的初始化。 ~~~ public DaoMaster(SQLiteDatabase db) { super(db, SCHEMA_VERSION); registerDaoClass(PersonDao.class); registerDaoClass(PeopleDao.class); registerDaoClass(IdCardDao.class); registerDaoClass(OrderDao.class); registerDaoClass(CourseDao.class); } ~~~ 显示调用父类的构造方法,接着registDaoClass() ~~~ 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); } ~~~ 看到,上面的一句很关键。new DaoConfig(); ~~~ public DaoConfig(SQLiteDatabase 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); } } ~~~ 这个方法就是完成DaoConfig的配置的,通过反射机制,获取到我们的Dao类,比如说PersonClass,具体的代码大家去看,就是通过反射,很好理解。注意statements是TableStatements类型的。 继续,newSession(); ~~~ public DaoSession newSession() { return new DaoSession(db, IdentityScopeType.Session, daoConfigMap); } ~~~ ~~~ public DaoSession(SQLiteDatabase db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap) { super(db); personDaoConfig = daoConfigMap.get(PersonDao.class).clone(); personDaoConfig.initIdentityScope(type); peopleDaoConfig = daoConfigMap.get(PeopleDao.class).clone(); peopleDaoConfig.initIdentityScope(type); idCardDaoConfig = daoConfigMap.get(IdCardDao.class).clone(); idCardDaoConfig.initIdentityScope(type); orderDaoConfig = daoConfigMap.get(OrderDao.class).clone(); orderDaoConfig.initIdentityScope(type); courseDaoConfig = daoConfigMap.get(CourseDao.class).clone(); courseDaoConfig.initIdentityScope(type); personDao = new PersonDao(personDaoConfig, this); peopleDao = new PeopleDao(peopleDaoConfig, this); idCardDao = new IdCardDao(idCardDaoConfig, this); orderDao = new OrderDao(orderDaoConfig, this); courseDao = new CourseDao(courseDaoConfig, this); registerDao(Person.class, personDao); registerDao(People.class, peopleDao); registerDao(IdCard.class, idCardDao); registerDao(Order.class, orderDao); registerDao(Course.class, courseDao); } ~~~ ~~~ 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); } } ~~~ 这个函数就是判断,类型范围的。一般我们不需要管。看到在DaoSession的构造函数中,根据在DaoMaster初始化的config,经过范围类型判断,在DaoSession中也初始化了。至此,初始化过程完毕。 - CURD过程 我们以insert为例 ~~~ dao.insert(person); ~~~ dao对象是我们初始化后得到的,person是一个Person实体对象。 ~~~ public long insert(T entity) { return executeInsert(entity, statements.getInsertStatement()); } ~~~ 上面的一段代码是AbstractDao类,这是一个抽象类,我们的Persondao就是继承的他。 ~~~ statements.getInsertStatement() ~~~ 通过statments对象实例获取SQLiteStatement对象,在(TableStatements类中) ~~~ public SQLiteStatement getInsertStatement() { if (insertStatement == null) { String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns); insertStatement = db.compileStatement(sql); } return insertStatement; } ~~~ 这样我们就获取到了一个SQLiteStatement对象。继续,看插入数据的过程。 ~~~ private long executeInsert(T entity, SQLiteStatement stmt) { long rowId; if (db.isDbLockedByCurrentThread()) { synchronized (stmt) { bindValues(stmt, entity); rowId = stmt.executeInsert(); } } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { synchronized (stmt) { bindValues(stmt, entity); rowId = stmt.executeInsert(); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } updateKeyAfterInsertAndAttach(entity, rowId, true); return rowId; } ~~~ 看到上面会判断是否在当前线程,不在的话会开启事务。总之,还是很安全的。就这么多吧,更多的源码还是大家自己看吧。真的感觉这个牛,坐等更新+上数据库更新