[关闭]
@zifeng328573112 2020-05-10T06:55:55.000000Z 字数 10638 阅读 552

GreenDao源码解析

1. 简介

[TOC]
greenDAO 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。

GreenDao模式

2. 特点

[TOC]
  • 性能最大化,可能是Android平台上最快的ORM框架;
  • 易于使用的API;
  • 最小的内存开销;
  • 依赖体积小;
  • 支持数据库加密;
  • 强大的社区支持。

3. 源码框架

[TOC]
GreenDao模式

GreenDao可以划分为四层:

GreenDao的实现原理进行分析,如下图所示:

GreenDao模式

其中:

4. DaoMaster

[TOC]
4.1:首先从DaoMaster的构造函数进行,需要传入一个SQLiteDatabase的实例。
  1. public DaoMaster(SQLiteDatabase db) {
  2. this(new StandardDatabase(db));
  3. }
  4. public DaoMaster(Database db) {
  5. super(db, SCHEMA_VERSION);
  6. registerDaoClass(BankInformationBeanDao.class);
  7. }

得到一个SQLiteDatabase的实例,则需要一个Helper类。在GreenDao中用到的

  1. $ DevOpenHelper->OpenHelper->DatabaseOpenHelper->SQLiteOpenHelper

DevOpenHelper继承OpenHelper类,里面有一个方法,更新版本。

  1. /** WARNING: Drops all table on Upgrade!
  2. * Use only during development.
  3. */
  4. public static class DevOpenHelper extends OpenHelper {
  5. public DevOpenHelper(Context context, String name) {
  6. super(context, name);
  7. }
  8. public DevOpenHelper(Context context, String name, CursorFactory factory) {
  9. super(context, name, factory);
  10. }
  11. @Override
  12. public void onUpgrade(Database db, int oldVersion, int newVersion) {
  13. Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
  14. dropAllTables(db, true);
  15. onCreate(db);
  16. }
  17. }

OpenHelper继承DatabaseOpenHelper类,里面也只有一个方法,创建一个表。

  1. /**
  2. * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
  3. */
  4. public static abstract class OpenHelper extends DatabaseOpenHelper {
  5. public OpenHelper(Context context, String name) {
  6. super(context, name, SCHEMA_VERSION);
  7. }
  8. public OpenHelper(Context context, String name, CursorFactory factory) {
  9. super(context, name, factory, SCHEMA_VERSION);
  10. }
  11. @Override
  12. public void onCreate(Database db) {
  13. Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
  14. createAllTables(db, false);
  15. }
  16. }

DatabaseOpenHelper继承自Sqlite帮助类,通过代理模式的思想,SQLiteDatabase扩展成Database。

  1. /**
  2. * Like {@link #getWritableDatabase()},
  3. * but returns a greenDAO abstraction of the database.
  4. * The backing DB is an standard {@link SQLiteDatabase}.
  5. */
  6. public Database getWritableDb() {
  7. return wrap(getWritableDatabase());
  8. }
  9. /**
  10. * Like {@link #getReadableDatabase()},
  11. * but returns a greenDAO abstraction of the database.
  12. * The backing DB is an standard {@link SQLiteDatabase}.
  13. */
  14. public Database getReadableDb() {
  15. return wrap(getReadableDatabase());
  16. }

StandardDatabase实现了Database接口,Database拥有SQLiteDatabase所有的功能方法,只是替换调用。相当于用代理的方式实现了SQLiteDatabase的所有方法。

DatabaseOpenHelper第二个功能是扩展了EncryptedHelper继承于net.sqlcipher.database.SQLiteOpenHelper,当我们需要加密的时候可以使用。

从构造函数中可以看出,在初始化时会将XXXDao类进行注册。DaoMaster继承AbstractDaoMaster,而真正的注册是在AbstractDaoMaster进行注册。

  1. /**
  2. * The master of dao will guide you:
  3. * start dao sessions with the master.
  4. * @author Markus
  5. */
  6. public abstract class AbstractDaoMaster {
  7. protected final Database db;
  8. protected final int schemaVersion;
  9. protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;
  10. public AbstractDaoMaster(Database db, int schemaVersion) {
  11. this.db = db;
  12. this.schemaVersion = schemaVersion;
  13. daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
  14. }
  15. protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
  16. DaoConfig daoConfig = new DaoConfig(db, daoClass);
  17. daoConfigMap.put(daoClass, daoConfig);
  18. }
  19. public int getSchemaVersion() {
  20. return schemaVersion;
  21. }
  22. /** Gets the SQLiteDatabase for custom database access.
  23. * Not needed for greenDAO entities.
  24. */
  25. public Database getDatabase() {
  26. return db;
  27. }
  28. public abstract AbstractDaoSession newSession();
  29. public abstract AbstractDaoSession newSession(IdentityScopeType type);
  30. }

以Class来作为key,对DaoConfig进行配置缓存绑定。DaoConfig将留在后面4.2说明。

DaoMaster还有一个作用是创建DaoSession。

  1. public DaoSession newSession() {
  2. return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
  3. }
  4. public DaoSession newSession(IdentityScopeType type) {
  5. return new DaoSession(db, type, daoConfigMap);
  6. }

4.2:DaoConfig
DaoConfig就是用来存储XXXDao中的一些必要的数据。

  1. $ public final class DaoConfig implements Cloneable {
  1. public final Database db;
  2. public final String tablename;
  3. public final Property[] properties;
  4. public final String[] allColumns;
  5. public final String[] pkColumns;
  6. public final String[] nonPkColumns;
  7. /** Single property PK or null if there's no PK or a multi property PK. */
  8. public final Property pkProperty;
  9. public final boolean keyIsNumeric;
  10. public final TableStatements statements;
  11. private IdentityScope<?, ?> identityScope;

这些属性对象是通过xxxDao.class反射来得到。

  1. private static Property[] reflectProperties(Class<? extends AbstractDao<?, ?>> daoClass) throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
  2. Class<?> propertiesClass = Class.forName(daoClass.getName() + "$Properties");
  3. Field[] fields = propertiesClass.getDeclaredFields();
  4. ArrayList<Property> propertyList = new ArrayList<Property>();
  5. final int modifierMask = Modifier.STATIC | Modifier.PUBLIC;
  6. for (Field field : fields) {
  7. // There might be other fields introduced by some tools, just ignore them (see issue #28)
  8. if ((field.getModifiers() & modifierMask) == modifierMask) {
  9. Object fieldValue = field.get(null);
  10. if (fieldValue instanceof Property) {
  11. propertyList.add((Property) fieldValue);
  12. }
  13. }
  14. }
  15. Property[] properties = new Property[propertyList.size()];
  16. for (Property property : propertyList) {
  17. if (properties[property.ordinal] != null) {
  18. throw new DaoException("Duplicate property ordinals");
  19. }
  20. properties[property.ordinal] = property;
  21. }
  22. return properties;
  23. }

IdentityScope表示是否需要缓存策略。缓存策略有两种:Session, None。当为None,表示不进行缓存,而为Session,则根据主键是否是数字来赋予内存缓存的管理对象。

  1. @SuppressWarnings("rawtypes")
  2. public void initIdentityScope(IdentityScopeType type) {
  3. if (type == IdentityScopeType.None) {
  4. identityScope = null;
  5. } else if (type == IdentityScopeType.Session) {
  6. if (keyIsNumeric) {
  7. identityScope = new IdentityScopeLong();
  8. } else {
  9. identityScope = new IdentityScopeObject();
  10. }
  11. } else {
  12. throw new IllegalArgumentException("Unsupported type: " + type);
  13. }
  14. }

其中在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的管理类。

  1. public DatabaseStatement getInsertStatement() {
  2. if (insertStatement == null) {
  3. String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns);
  4. DatabaseStatement newInsertStatement = db.compileStatement(sql);
  5. synchronized (this) {
  6. if (insertStatement == null) {
  7. insertStatement = newInsertStatement;
  8. }
  9. }
  10. if (insertStatement != newInsertStatement) {
  11. newInsertStatement.close();
  12. }
  13. }
  14. return insertStatement;
  15. }

可以看出,TableStatements维护着一个insertStatement对象,当对象为null时,调用SqlUtils的createSqlInsert的方法将这些参数拼接成sql语句。如果不为null就直接返回,为null就拼接创建,以达到复用优化性能的作用。

  1. /**
  2. * Insert an entity into the table associated with a concrete DAO.
  3. *
  4. * @return row ID of newly inserted entity
  5. */
  6. public long insertOrReplace(T entity) {
  7. return executeInsert(entity, statements.getInsertOrReplaceStatement(), true);
  8. }
  9. private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
  10. long rowId;
  11. if (db.isDbLockedByCurrentThread()) {
  12. rowId = insertInsideTx(entity, stmt);
  13. } else {
  14. // Do TX to acquire a connection before locking the stmt to avoid deadlocks
  15. db.beginTransaction();
  16. try {
  17. rowId = insertInsideTx(entity, stmt);
  18. db.setTransactionSuccessful();
  19. } finally {
  20. db.endTransaction();
  21. }
  22. }
  23. if (setKeyAndAttach) {
  24. updateKeyAfterInsertAndAttach(entity, rowId, true);
  25. }
  26. return rowId;
  27. }
  28. private long insertInsideTx(T entity, DatabaseStatement stmt) {
  29. synchronized (stmt) {
  30. if (isStandardSQLite) {
  31. SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
  32. bindValues(rawStmt, entity);
  33. return rawStmt.executeInsert();
  34. } else {
  35. bindValues(stmt, entity);
  36. return stmt.executeInsert();
  37. }
  38. }
  39. }
  40. protected void updateKeyAfterInsertAndAttach(T entity, long rowId, boolean lock) {
  41. if (rowId != -1) {
  42. K key = updateKeyAfterInsert(entity, rowId);
  43. attachEntity(key, entity, lock);
  44. } else {
  45. // TODO When does this actually happen? Should we throw instead?
  46. DaoLog.w("Could not insert row (executeInsert returned -1)");
  47. }
  48. }
  1. /**
  2. * Attaches the entity to the identity scope.
  3. * Calls attachEntity(T entity).
  4. * @param key Needed only for identity scope,
  5. * pass null if there's none.
  6. * @param entity The entitiy to attach
  7. */
  8. protected final void attachEntity(K key, T entity, boolean lock) {
  9. attachEntity(entity);
  10. if (identityScope != null && key != null) {
  11. if (lock) {
  12. identityScope.put(key, entity);
  13. } else {
  14. identityScope.putNoLock(key, entity);
  15. }
  16. }
  17. }

5. DaoSession

[TOC]
主要是创建xxxDao对象,然后将xxxDao注册到映射表。
  1. public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap) {
  2. super(db);
  3. bankInformationBeanDaoConfig = daoConfigMap.get(BankInformationBeanDao.class).clone();
  4. bankInformationBeanDaoConfig.initIdentityScope(type);
  5. bankInformationBeanDao = new BankInformationBeanDao(bankInformationBeanDaoConfig, this);
  6. registerDao(BankInformationBean.class, bankInformationBeanDao);
  7. }

并且我们可以通过DaoSession实例直接得到xxxDao。

5.1: AbstractDaoSession
AbstractDaoSession里面就是实现一个从映射表中取出AbstractDao对象来进行增删改查的操作。

6. BankInformationBeanDao和AbstractDao

[TOC]
DAOs是负责直接实现增删改查数据库的,直接映射着我们要操作的JeanBean,所以默认情况下命名为XxxDAO。简而言之,要想操作数据库,都得先通过DaoSession的getXxxDAO系方法获得对应的DAO,再调用对应的方法, DAOs里生成数据库语句, 将成员变量与表里的参数绑定对应起来, 并且在它的父类AbstractDao 里实现了相对DaoSession的操作数据的方法。

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() 模糊查询
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注