@zifeng328573112
2020-05-10T06:55:55.000000Z
字数 10638
阅读 771

[TOC]

GreenDao可以划分为四层:
GreenDao的实现原理进行分析,如下图所示:

其中:
public DaoMaster(SQLiteDatabase db) {this(new StandardDatabase(db));}public DaoMaster(Database db) {super(db, SCHEMA_VERSION);registerDaoClass(BankInformationBeanDao.class);}
得到一个SQLiteDatabase的实例,则需要一个Helper类。在GreenDao中用到的
$ DevOpenHelper->OpenHelper->DatabaseOpenHelper->SQLiteOpenHelper
DevOpenHelper继承OpenHelper类,里面有一个方法,更新版本。
/** 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);}@Overridepublic 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);}}
OpenHelper继承DatabaseOpenHelper类,里面也只有一个方法,创建一个表。
/*** 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);}@Overridepublic void onCreate(Database db) {Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);createAllTables(db, false);}}
DatabaseOpenHelper继承自Sqlite帮助类,通过代理模式的思想,SQLiteDatabase扩展成Database。
/*** Like {@link #getWritableDatabase()},* but returns a greenDAO abstraction of the database.* The backing DB is an standard {@link SQLiteDatabase}.*/public Database getWritableDb() {return wrap(getWritableDatabase());}/*** Like {@link #getReadableDatabase()},* but returns a greenDAO abstraction of the database.* The backing DB is an standard {@link SQLiteDatabase}.*/public Database getReadableDb() {return wrap(getReadableDatabase());}
StandardDatabase实现了Database接口,Database拥有SQLiteDatabase所有的功能方法,只是替换调用。相当于用代理的方式实现了SQLiteDatabase的所有方法。
DatabaseOpenHelper第二个功能是扩展了EncryptedHelper继承于net.sqlcipher.database.SQLiteOpenHelper,当我们需要加密的时候可以使用。
从构造函数中可以看出,在初始化时会将XXXDao类进行注册。DaoMaster继承AbstractDaoMaster,而真正的注册是在AbstractDaoMaster进行注册。
/*** The master of dao will guide you:* start dao sessions with the master.* @author Markus*/public abstract class AbstractDaoMaster {protected final Database db;protected final int schemaVersion;protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;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);}public int getSchemaVersion() {return schemaVersion;}/** Gets the SQLiteDatabase for custom database access.* Not needed for greenDAO entities.*/public Database getDatabase() {return db;}public abstract AbstractDaoSession newSession();public abstract AbstractDaoSession newSession(IdentityScopeType type);}
以Class来作为key,对DaoConfig进行配置缓存绑定。DaoConfig将留在后面4.2说明。
DaoMaster还有一个作用是创建DaoSession。
public DaoSession newSession() {return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);}public DaoSession newSession(IdentityScopeType type) {return new DaoSession(db, type, daoConfigMap);}
4.2:DaoConfig
DaoConfig就是用来存储XXXDao中的一些必要的数据。
$ 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;
这些属性对象是通过xxxDao.class反射来得到。
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;}
IdentityScope表示是否需要缓存策略。缓存策略有两种:Session, None。当为None,表示不进行缓存,而为Session,则根据主键是否是数字来赋予内存缓存的管理对象。
@SuppressWarnings("rawtypes")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);}}
其中在IdentityScopeObject类中,直接用HashMap,以主键为Key,一个存储实体的队列为值,而在IdentityScopeLong则用LongHashMap,里面也是以主键为Key,一个存储实体的队列为值。
LongHashMap拥有containsKey(long key)、get(long key)、put(long key, T value)、remove(long key)等方法,与HashMap不同的是,LongHashMap的阈值是默认容量的4/3倍。超过该阈值时进行两倍扩容;而HashMap则是有一个扩容因子为0.75,如果超过默认容量的0.75倍,也进行两倍扩容。
另一个重要的属性对象则是Property,放在3.2节进行讲解。
4.3:Property
该类的主要作用是将bean类中的成员变量封装成一个对象,然后可以直接将这些对象进行一些条件查询。封装成对象主要是在xxxDao内进行封装。
| 条件查询 | 符号 |
|---|---|
| eq | = |
| notEq | <> |
| like | |
| between | |
| in | |
| notIn | |
| gt | > |
| lt | < |
| ge | 》= |
| isNull | |
| le | <= |
| isNotNull |
4.4:TableStatements
GreenDao不管是做查询还是其他操作都是使用Statement来进行优化性能的,而且Statement是可以重用的。而TableStatements是Statement的管理类。
public DatabaseStatement getInsertStatement() {if (insertStatement == null) {String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns);DatabaseStatement newInsertStatement = db.compileStatement(sql);synchronized (this) {if (insertStatement == null) {insertStatement = newInsertStatement;}}if (insertStatement != newInsertStatement) {newInsertStatement.close();}}return insertStatement;}
可以看出,TableStatements维护着一个insertStatement对象,当对象为null时,调用SqlUtils的createSqlInsert的方法将这些参数拼接成sql语句。如果不为null就直接返回,为null就拼接创建,以达到复用优化性能的作用。
/*** Insert an entity into the table associated with a concrete DAO.** @return row ID of newly inserted entity*/public long insertOrReplace(T entity) {return executeInsert(entity, statements.getInsertOrReplaceStatement(), 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 deadlocksdb.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();}}}protected void updateKeyAfterInsertAndAttach(T entity, long rowId, boolean lock) {if (rowId != -1) {K key = updateKeyAfterInsert(entity, rowId);attachEntity(key, entity, lock);} else {// TODO When does this actually happen? Should we throw instead?DaoLog.w("Could not insert row (executeInsert returned -1)");}}
/*** Attaches the entity to the identity scope.* Calls attachEntity(T entity).* @param key Needed only for identity scope,* pass null if there's none.* @param entity The entitiy to attach*/protected final void attachEntity(K key, T entity, boolean lock) {attachEntity(entity);if (identityScope != null && key != null) {if (lock) {identityScope.put(key, entity);} else {identityScope.putNoLock(key, entity);}}}
插入后的善后处理,这里就是更新实体的ID为RowId和做内存缓存。
上面的操作是单个实体的插入,当使用多数据插入的时候,需要加锁,因为多数据插入需要耗时,所以加锁是为了防止多线程问题。
GreenDao是怎么优化和做缓存
a、通过Statement的复用,达到优化的效果,这是所有数据库都通用的。
b、通过Key映射保存到内存中,保存的值当前是软引用,要不很容易爆表。
DaoSession和AbstractDaoSession
DaoSession 管理着所有的DAO对象主要用于提供获取DAOs的接口,每一个DaoMaster持有一个数据库连接,通过DaoMaster的newSession()方法可以实例化多个Session,这些Session对应同一个数据库连接,但是系统会为每一个Session分配内存,在这片内存中会为实体进行缓存。每一个Session对应一个Identity scope。一个新的Session就代表一个会话,通过同一个会话中的DAOs进行的数据库操作。
public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap) {super(db);bankInformationBeanDaoConfig = daoConfigMap.get(BankInformationBeanDao.class).clone();bankInformationBeanDaoConfig.initIdentityScope(type);bankInformationBeanDao = new BankInformationBeanDao(bankInformationBeanDaoConfig, this);registerDao(BankInformationBean.class, bankInformationBeanDao);}
并且我们可以通过DaoSession实例直接得到xxxDao。
5.1: AbstractDaoSession
AbstractDaoSession里面就是实现一个从映射表中取出AbstractDao对象来进行增删改查的操作。
6.1:xxxDao
6.2:AbstractDao
AbstractDao 里实现了相对DaoSession的操作数据的方法。
【增】
插入单个实体:insert(T)
插入或代替单个实体:insertOrReplace(T)
插入没有设置主键的单个实体:insertWithoutSettingPK(T)
数组的插入:insertInTX(T... entities)
集合实体的插入:insertInTx(Iterable)
插入或更新数组:insertInReplaceInTx(T... entities)
插入或更新集合:insertInReplaceInTx(Iterable)
【删】
删除单个实体:delete(T entity)
删除全部:deleteAll()
通过主键进行删除:deleteByKey(K key)
删除一个数组:deleteInTx(T... entities)
删除一个集合:deleteInTx(Iterable entities)
通过key删除一个数组:deleteByKeyInTx(K... keys)
通过key删除一个集合:deleteByKeyInTx(Iterable keys)
【改】
更新一个实体:update(T entity)
更新一个数组:updateInTx(T... entities)
更新一个集合:updateInTx(Iterable entities)
【查】
通过queryBuilder对象进行查询:queryBuilder();
查询的规则有以下几种:
| 查询条件 | 注释 |
|---|---|
| where | 条件子句,相当于SQL中的Where |
| distinct | 去重查询,相当于是在SQL语句中加了distinct |
| whereOr | 或条件查询 |
| and | 且条件查询 |
| orderAsc | 升序或降序查询 |
| limit | 分页查询 |
| list | 执行Query并把返回集合,并且entity都会加载到内存中,返回的结果通常就是ArrayList |
| listLazy | Entity按需加载到内存中,在第一次访问list中的element时,它就会被加载并且缓存 |
| listLazyUncached | 每次访问结果集的时候都是从数据库中加载,而不使用缓存。 |
| listIterator() | 通过迭代器访问结果集,并且采用的时lazy-loading,结果不会缓存。 |
| like() | 模糊查询 |