在上一篇GreenDao详解一中有讲到,GreenDao
在build
的时候,会自动生成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
操作。为了直观,这里给出了一个关系图:
DaoMaster
DaoMaster
是对数据库进行相关操作的一个封装类,内部包括了创建表,删除表的方法,还包括两个静态抽象类OpenHelper
和DevOpenHelper
,OpenHelper
继承于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详解一已经简单说明了,在这里就不一一重复了。