上一篇我们知道,GreenDao利用FreeMarker帮我们产生了数据库辅助类,那么以这几个类怎么用为入口,进行我们的源码分析,look代码,来一个简单的条件查询
DaoMaster.DevOpenHelper d=new DaoMaster.DevOpenHelper(MainActivity.this, "sq");
DaoMaster m=new DaoMaster(d.getWritableDatabase());
DaoSession daoSession=m.newSession();
CustomerDao dao=daoSession.getCustomerDao();
List<Customer> listCus=dao.queryBuilder().where(CustomerDao.Properties.Id.eq(1)).list();
不管我们添加了几个表映射,都会产生一个 DaoMaster类,一个 DaoSession类,和多个表映射类和响应的Dao操作类,看代码流程大约可以看出,如果我们想要操作一个表的话首先得得到 DaoMaster,然后通过DaoMaster得到 DaoSession,相当于一个会话,从这个会话中,我们可以得到不同表的操作类Dao。
首先看一下DevOpenHelper是干嘛的?
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);
}
/**
* 这个方法怎么这么熟悉,这不是操作sqlite的数据库升级的回调接口吗
*/
@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);
}
}
点进这个静态内部类,发现了一个非常熟悉的类,额猜一下,那么这个静态内部类就是用来操作Sqlite是的。
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);
}
}
进入父类又有一个父类,擦
/**
* 这个是啥不就是操作sqlite的原生类吗
* @author Administrator
*
*/
public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {
private final Context context;
private final String name;
private final int version;
private EncryptedHelper encryptedHelper;
private boolean loadSQLCipherNativeLibs = true;
public DatabaseOpenHelper(Context context, String name, int version) {
this(context, name, null, version);
}
public DatabaseOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
this.context = context;
this.name = name;
this.version = version;
}
果然不出所料,最终就是通过android的原生操作Sqlite的
/**
* 得到包装读、写
*/
public Database getWritableDb() {
return wrap(getWritableDatabase());
}
/**
* 得到包装的读类
*
* @return
*/
public Database getReadableDb() {
return wrap(getReadableDatabase());
}
其中这两个方法为我们返回原生的操作数据库的类,而在原有数据类的基础上包装了一个代理类,不用说肯定是干点坏事了
*
* @param sqLiteDatabase
* 包装一下数据库操作类,
* @return
*/
protected Database wrap(SQLiteDatabase sqLiteDatabase) {
return new StandardDatabase(sqLiteDatabase);
}
向下面这个代理,为客户端返回加密的数据库操作类,用的是开源的
SQLCipher
/**
用 SQLCipher进行加密
*/
public Database getEncryptedWritableDb(String password) {
EncryptedHelper encryptedHelper = checkEncryptedHelper();
return encryptedHelper.wrap(encryptedHelper
.getWritableDatabase(password));
}
/**
/**
用 SQLCipher进行加密
*/
public Database getEncryptedWritableDb(char[] password) {
EncryptedHelper encryptedHelper = checkEncryptedHelper();
return encryptedHelper.wrap(encryptedHelper
.getWritableDatabase(password));
}
最终返回的是SQLCipher提供的类
private class EncryptedHelper extends
net.sqlcipher.database.SQLiteOpenHelper {
public EncryptedHelper(Context context, String name, int version,
boolean loadLibs) {
super(context, name, null, version);
if (loadLibs) {
net.sqlcipher.database.SQLiteDatabase.loadLibs(context);
}
}
@Override
public void onCreate(net.sqlcipher.database.SQLiteDatabase db) {
DatabaseOpenHelper.this.onCreate(wrap(db));
}
@Override
public void onUpgrade(net.sqlcipher.database.SQLiteDatabase db,
int oldVersion, int newVersion) {
DatabaseOpenHelper.this.onUpgrade(wrap(db), oldVersion, newVersion);
}
@Override
public void onOpen(net.sqlcipher.database.SQLiteDatabase db) {
DatabaseOpenHelper.this.onOpen(wrap(db));
}
protected Database wrap(
net.sqlcipher.database.SQLiteDatabase sqLiteDatabase) {
return new EncryptedDatabase(sqLiteDatabase);
}
}
对SQLCipher不熟悉的可以去官方查看用法
SQLCipher
接下来就涉及到DaoMaster的初始化了
public DaoMaster(Database db) {
super(db, SCHEMA_VERSION);
registerDaoClass(NoteDao.class);
registerDaoClass(CustomerDao.class);
registerDaoClass(OrderDao.class);
}
此处SCHEMA_VERSION的值为3,记得上一篇我们设置数据库版本号为3,初始化的DaoMaster持有一个数据库操作对象,那么接下来开始注册表的操作类,有多少个表就注册多少次
protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
DaoConfig daoConfig = new DaoConfig(db, daoClass);
daoConfigMap.put(daoClass, daoConfig);
}
最终就是把Dao类保存进集合中,接下来看一下 DaoSession
public DaoSession newSession() {
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
创建这个会话类传入数据库操作对象和保存 Dao的集合的引用,接着看一下DaoSession类的构造方法都做了什么
public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
daoConfigMap) {
super(db);
noteDaoConfig = daoConfigMap.get(NoteDao.class).clone();
noteDaoConfig.initIdentityScope(type);
customerDaoConfig = daoConfigMap.get(CustomerDao.class).clone();
customerDaoConfig.initIdentityScope(type);
orderDaoConfig = daoConfigMap.get(OrderDao.class).clone();
orderDaoConfig.initIdentityScope(type);
noteDao = new NoteDao(noteDaoConfig, this);
customerDao = new CustomerDao(customerDaoConfig, this);
orderDao = new OrderDao(orderDaoConfig, this);
registerDao(Note.class, noteDao);
registerDao(Customer.class, customerDao);
registerDao(Order.class, orderDao);
}
这个构造函数主要做了创建各个表的操作对象,然后和表的映射类绑定存到集合中,并且将参数属性全部放进Config与Dao类绑定。既然表操作的Dao类都创建了,那么接下来增删改查都用dao来操作,先看一下插入 dao.insert(entity),进入
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 {
//开启事务插入数据
db.beginTransaction();
try {
rowId = insertInsideTx(entity, stmt);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
/**
* 插入完之后保存插入的行号,行号就是自动增长的id
*/
if (setKeyAndAttach) {
updateKeyAfterInsertAndAttach(entity, rowId, true);
}
return rowId;
}
这里出现了一个关键的对象
DatabaseStatement,这个对象是干嘛的呢?上边提到一个普通的数据库类和一个加密的数据库类,sqlcipher此篇幅暂且不讨论,那么DatabaseStatement的实现类为
public class StandardDatabaseStatement implements DatabaseStatement {
private final SQLiteStatement delegate;
public StandardDatabaseStatement(SQLiteStatement delegate) {
this.delegate = delegate;
}
用到了静态代理,真正操作数据库的是SQLiteStatement ,android自带的数据库操作类,那么又有问题了为什么不用sqlite的最常用的插入语句呢,比如
sqliteDatabase.insert(table, nullColumnHack, values);
我看一下源码怎么实现的
public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
acquireReference();
try {
...此处省略30行代码
SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
try {
return statement.executeInsert();
} finally {
statement.close();
}
} finally {
releaseReference();
}
}
源码最终还是创建了SQLiteStatement对象来操作数据库,那么GreenDao的第二个数据库操作快的优势体现出来了,用SQLiteStatement的话,代码执行量就少了很多,少一行代码,速度就快上毫秒级,肯定同样的功能代码量少的实现方式会快一些,那么积少成多,操作数据千条万条的时候,速度就明显的被看出来,看一下直接执行sql语句是不是也是创建了此对象
sqliteDatabase.execSQL(sql);
private int executeSql(String sql, Object[] bindArgs) throws SQLException {
acquireReference();
try {
if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
boolean disableWal = false;
synchronized (mLock) {
if (!mHasAttachedDbsLocked) {
mHasAttachedDbsLocked = true;
disableWal = true;
}
}
if (disableWal) {
disableWriteAheadLogging();
}
}
//没错又是它
SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
try {
return statement.executeUpdateDelete();
} finally {
statement.close();
}
} finally {
releaseReference();
}
}
没错又是这个家伙,这就是GreenDao比起其他数据库操作框架快的原因之一了吧。
private long insertInsideTx(T entity, DatabaseStatement stmt) {
synchronized (stmt) {
if (isStandardSQLite) {
/**
* 得到SQLiteStatement
*/
SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
/**
* 绑定数据
*/
bindValues(rawStmt, entity);
//执行插入操作
return rawStmt.executeInsert();
} else {
bindValues(stmt, entity);
return stmt.executeInsert();
}
}
}
这个方法是将对象的数据绑定到第几列数据
protected final void bindValues(DatabaseStatement stmt, Customer entity) {
//清除绑定
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
stmt.bindString(2, entity.getName());
}
最终awStmt.executeInsert()将保存到SQLiteStatement的数据插入到数据库表中,插入数据看完了,看看改数据
public void update(T entity) {
assertSinglePk();
DatabaseStatement stmt = statements.getUpdateStatement();
if (db.isDbLockedByCurrentThread()) {
synchronized (stmt) {
if (isStandardSQLite) {
updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true);
} else {
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();
}
}
}
进入updateInsideSynchronized方法
protected void updateInsideSynchronized(T entity, SQLiteStatement stmt, boolean lock) {
// To do? Check if it's worth not to bind PKs here (performance).
//绑定数据
bindValues(stmt, entity);
int index = config.allColumns.length + 1;
K key = getKey(entity);
if (key instanceof Long) {
stmt.bindLong(index, (Long) key);
} else if (key == null) {
throw new DaoException("Cannot update entity without key - was it inserted before?");
} else {
stmt.bindString(index, key.toString());
}
//执行更新操作
stmt.execute();
attachEntity(key, entity, lock);
}
和插入类似,只不过检查了key值必须存在,delete删除方法也类似,最后看一下查询方法
List<Customer> listCus=dao.queryBuilder().where(CustomerDao.Properties.Id.eq(1)).list();
这句代码查询出id等于1的customer的一行数据,其中queryBuilder()方法创建查询的辅助类
/**
* 创建查询查询的辅助类
* @return
*/
public QueryBuilder<T> queryBuilder() {
return QueryBuilder.internalCreate(this);
}
看命名是使用了构造者模式,接着进入internalCreate方法
public static <T2> QueryBuilder<T2> internalCreate(AbstractDao<T2, ?> dao) {
return new QueryBuilder<T2>(dao);
}
继续看where(CustomerDao.Properties.Id.eq(1))
/*
* 保存条件进集合
*/
public QueryBuilder<T> where(WhereCondition cond, WhereCondition... condMore) {
whereCollector.add(cond, condMore);
return this;
}
看完这一个方法后,大体可以猜测到QueryBuilder这个类主要用于暂时保存需要查询的拼接参数,而CustomerDao.Properties.Id.eq(1)返回PropertyCondition对象用于保存查询条件的类,那么真正实现查询的是list()方法了,看到这我们可以大胆的猜测一下,下一步就是将保存在QueryBuilder中的参数进行拼接转化为sql语句,然后进行查询了
/**
* 拼接sql语句
* @return
*/
public Query<T> build() {
StringBuilder builder = createSelectBuilder();
int limitPosition = checkAddLimit(builder);
int offsetPosition = checkAddOffset(builder);
String sql = builder.toString();
checkLog(sql);
return Query.create(dao, sql, values.toArray(), limitPosition, offsetPosition);
}
private void appendJoinsAndWheres(StringBuilder builder, String tablePrefixOrNull) {
values.clear();
for (Join<T, ?> join : joins) {
builder.append(" JOIN ").append(join.daoDestination.getTablename()).append(' ');
builder.append(join.tablePrefix).append(" ON ");
SqlUtils.appendProperty(builder, join.sourceTablePrefix, join.joinPropertySource).append('=');
SqlUtils.appendProperty(builder, join.tablePrefix, join.joinPropertyDestination);
}
boolean whereAppended = !whereCollector.isEmpty();
if (whereAppended) {
builder.append(" WHERE ");
whereCollector.appendWhereClause(builder, tablePrefixOrNull, values);
}
for (Join<T, ?> join : joins) {
if (!join.whereCollector.isEmpty()) {
if (!whereAppended) {
builder.append(" WHERE ");
whereAppended = true;
} else {
builder.append(" AND ");
}
join.whereCollector.appendWhereClause(builder, join.tablePrefix, values);
}
}
}
所有的方法都证明了QueryBuilder就是用来拼接sql语句的,那么继续
public List<T> list() {
checkThread();
/**
* 开始查询数据
*/
Cursor cursor = dao.getDatabase().rawQuery(sql, parameters);
//将数据赋值给对象
return daoAccess.loadAllAndCloseCursor(cursor);
}
public Customer readEntity(Cursor cursor, int offset) {
Customer entity = new Customer( //
cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
cursor.getString(offset + 1) // name
);
return entity;
}
果然像我们猜想的那样,到此sqlite数据库操作流程分析就完成了,现在可以试着写一个自己的数据库操作库了。