前言
GreenDao和Room都是Android上常用的数据库框架,本篇来分析一下GreenDao的源码。
简单使用
添加依赖
//1、根build.gradle,添加GreenDao插件依赖
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.4'
//GreenDao
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
//2、app模块的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'
3、引入Gradle插件以及依赖
dependencies {
//GreenDao
implementation 'org.greenrobot:greendao:3.3.0'
//可选,加密型数据库依赖
implementation 'net.zetetic:android-database-sqlcipher:3.5.9'
}
4、配置GreenDao,可选
greendao {
schemaVersion 1
//指定生成的类的目录,默认生成在build目录中
daoPackage 'com.hzh.green.dao.sample.db'
targetGenDir 'src/main/java'
}
创建数据库表实体
- @Entity注解,声明一个实体类为数据库表实体,
nameInDb
属性可指定对应的表名 - @Id注解,声明某个属性是这个表的主键
- @Property注解,声明实体的某个成员属性对应表的某个字段名,
nameInDb
属性可指定表的字段名 - @NotNull注解,声明这个字段为非空
@Entity(nameInDb = "person_info")
public class PersonInfoEntity {
@Id
private String id;
@Property(nameInDb = "create_time")
@NotNull
private Date createTime = new Date();
@Property(nameInDb = "update_time")
@NotNull
private Date updateTime = new Date();
/**
* 删除标识,0:未删除,1:删除
*/
@Property(nameInDb = "delete_flag")
@NotNull
private Integer deleteFlag = 0;
/**
* 版本号,默认1
*/
@Property(nameInDb = "version")
@NotNull
private Integer version = 1;
//-------------------- 自定义属性 --------------------
@Property(nameInDb = "person_name")
@NotNull
private String personName;
@Property(nameInDb = "sex")
@NotNull
private String sex;
@Property(nameInDb = "age")
@NotNull
private Integer age;
//省略getter、setter方法
}
生成Dao
写完实体后,手动build一下,在项目的build => generated => source => greendao => 包名
下,会生成这几个类,分别是:DaoMaster
、DaoSession
、PersonInfoEntityDao
- DaoMaster,数据库层的总入口,Dao的主人以及负责生辰
DaoSession
,内部有2个抽象类,OpenHelper
、DevOpenHelper
,OpenHelper
继承于DatabaseOpenHelper
,而DevOpenHelper
继承于OpenHelper
,而DatabaseOpenHelper
又继承于Android的SQLiteOpenHelper
,并重写了它的某些方法 - DaoSession,会话层,用于生成Dao、注册Dao、以及Dao的CRUD方法
- Dao,数据库实体对应的Dao层对象,拥有表的配置信息,也有部分的CRUD方法
开始使用
- 自定义SQLiteOpenHelper
该类继承于DaoMaster.OpenHelper
,处理数据库版本升级
public class SampleSQLiteOpenHelper extends DaoMaster.OpenHelper {
private static final String TAG = SampleSQLiteOpenHelper.class.getSimpleName();
public SampleSQLiteOpenHelper(Context context, String name) {
super(context, name);
}
public SampleSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
super.onUpgrade(db, oldVersion, newVersion);
if (oldVersion == newVersion) {
Log.i(TAG, "数据库无需升级");
return;
}
Log.i(TAG, "数据库从" + oldVersion + "升级到 ::: " + newVersion + "版本");
//处理升级...
}
}
- 创建单例类GreenDaoManager,获取DaoMaster和DaoSession
public class GreenDaoManager {
private static final String DB_NAME = "app_db";
private static volatile GreenDaoManager mInstance;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
private GreenDaoManager() {
if (mInstance == null) {
SampleSQLiteOpenHelper openHelper = new SampleSQLiteOpenHelper(AppContext.getInstance(), DB_NAME);
SQLiteDatabase db = openHelper.getWritableDatabase();
mDaoMaster = new DaoMaster(db);
mDaoSession = mDaoMaster.newSession();
}
}
public static void init() {
getInstance();
}
public static GreenDaoManager getInstance() {
if (mInstance == null) {
synchronized (GreenDaoManager.class) {
if (mInstance == null) {
mInstance = new GreenDaoManager();
}
}
}
return mInstance;
}
public DaoMaster getDaoMaster() {
return mDaoMaster;
}
public DaoSession getDaoSession() {
return mDaoSession;
}
}
- 获取Dao,并进行CRUD
//获取Dao对象
PersonInfoEntityDao dao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao();
//增
dao.insert(entity);
//删
dao.delete(entity);
//查
dao.loadAll();
//改
dao.update(entity);
浅窥代码
DaoMaster,作为数据库框架的总入口,里面存在2个抽象类,分别是OpenHelper
和DevOpenHelper
,其中DevOpenHelper
继承于OpenHelper
,OpenHelper
继承于DatabaseOpenHelper
,它又继承于Android的SQLiteOpenHelper
- OpenHelper,在onCreate()中,就是数据库创建时,通过
createAllTables(db, ifNotExists)
,创建所有表,而onUpgrade()
没有被重写,开发者需要重写该方法,进行当数据库升级时,表结构的一些变化对应的处理 - DevOpenHelper,因为继承于
OpenHelper
,所以onCreate()
方法走的也是OpenHelper
的创建所有表,但重写了onUpgrade()
,并在其中调用dropAllTables(db, ifNotExists)
来删除所有表,再调一次onCreate()
来重新创建表,所以数据会丢失,一般用于开发过程中使用
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) {
PersonInfoEntityDao.createTable(db, ifNotExists);
}
/** Drops underlying database table using DAOs. */
//删除所有表
public static void dropAllTables(Database db, boolean ifExists) {
PersonInfoEntityDao.dropTable(db, ifExists);
}
//...
/**
* 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);
}
}
}
DatabaseOpenHelper
DatabaseOpenHelper继承于SQLiteOpenHelper
,所以它也是一个数据库帮助类,它提供了获取2种类型的数据库,分别是获取StandardDatabase
标准型数据库,获取EncryptedDatabase
加密型数据库
这里使用了代理模式,通过wrap()
方法,把不同类型数据库的SQLiteDatabase
包装为统一的Database
接口,分别是StandardDatabase
和EncryptedDatabase
public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {
//...
//获取标准的可写数据库
public Database getWritableDb() {
return wrap(getWritableDatabase());
}
//获取标准的可读数据库
public Database getReadableDb() {
return wrap(getReadableDatabase());
}
//包装,SQLiteDatabase为StandardDatabase,并返回StandardDatabase的基础接口Database
protected Database wrap(SQLiteDatabase sqLiteDatabase) {
return new StandardDatabase(sqLiteDatabase);
}
//获取可读的加密数据库
public Database getEncryptedWritableDb(String password) {
EncryptedHelper encryptedHelper = checkEncryptedHelper();
return encryptedHelper.getEncryptedWritableDb(password);
}
//获取可写的加密数据库
public Database getEncryptedWritableDb(char[] password) {
EncryptedHelper encryptedHelper = checkEncryptedHelper();
return encryptedHelper.getEncryptedWritableDb(password);
}
interface EncryptedHelper {
Database getEncryptedReadableDb(String password);
Database getEncryptedReadableDb(char[] password);
Database getEncryptedWritableDb(String password);
Database getEncryptedWritableDb(char[] password);
}
class SqlCipherEncryptedHelper extends net.sqlcipher.database.SQLiteOpenHelper implements DatabaseOpenHelper.EncryptedHelper {
private Database wrap(SQLiteDatabase sqLiteDatabase) {
return new EncryptedDatabase(sqLiteDatabase);
}
}
//...
}
StandardDatabase、EncryptedDatabase
StandardDatabase
和EncryptedDatabase
,分别都代理了各自的SQLiteDatabase
,统一了API。
加密型数据库,是依赖了sqlcipher
第三方加密型数据库。如果需要创建加密型数据库,添加好依赖后,把获取DB对象的getWritableDb()
方法,换成getEncryptedWritableDb()
即可,无需做其他改动,这就是代理模式的好处
//标准型数据库,StandardDatabase
public class StandardDatabase implements Database {
//这个SQLiteDatabase,是android.database.sqlite.SQLiteDatabase包下的
private final SQLiteDatabase delegate;
public StandardDatabase(SQLiteDatabase delegate) {
this.delegate = delegate;
}
@Override
public Cursor rawQuery(String sql, String[] selectionArgs) {
return delegate.rawQuery(sql, selectionArgs);
}
@Override
public void execSQL(String sql) throws SQLException {
delegate.execSQL(sql);
}
//...
}
//加密型数据库,EncryptedDatabase
public class EncryptedDatabase implements Database {
//这个SQLiteDatabase,是net.sqlcipher.database包下的
private final SQLiteDatabase delegate;
public EncryptedDatabase(SQLiteDatabase delegate) {
this.delegate = delegate;
}
@Override
public Cursor rawQuery(String sql, String[] selectionArgs) {
return delegate.rawQuery(sql, selectionArgs);
}
@Override
public void execSQL(String sql) throws SQLException {
delegate.execSQL(sql);
}
}
源码分析
DaoMaster
DaoMaster
继承于AbstractDaoMaster
,构造方法需要传入SQLiteDatabase
实例,例如通过getWritableDatabase()
获取到,传入就可以获取一个DaoMaster
实例,内部就会创建一个标准型数据的代理类StandardDatabase
,同时通过registerDaoClass()
注册Dao
AbstractDaoMaster
基类的构造方法中,保存了Database
代理类、版本号外,还创建了一个名为daoConfigMap
,类型为Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
的HashMap,这个Map负责注册Dao和DaoConfig的映射关系,而后续的DaoSession
,就会通过这个daoConfigMap
,获取对应Dao的DaoConfig
来创建Dao实例
public class DaoMaster extends AbstractDaoMaster {
//...
public DaoMaster(SQLiteDatabase db) {
this(new StandardDatabase(db));
}
public DaoMaster(Database db) {
super(db, SCHEMA_VERSION);
registerDaoClass(PersonInfoEntityDao.class);
}
//...
}
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);
}
}
DaoSession
DaoSession,作为会话层对象,负责创建Dao、注册Dao,以及提供一些CRUD的能力
通过DaoMaster
的newSession()
方法,就可以创建一个DaoSession
实例
//创建DaoSession
DaoSession daoSession = daoMaster.newSession();
public class DaoMaster extends AbstractDaoMaster {
//...
public DaoSession newSession() {
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
//...
}
- DaoSession的构造方法
DaoSession继承于AbstractDaoSession
,DaoSession的构造方法,首先super(db)
调用父类的构造方法
AbstractDaoSession的构造方法,创建了一个名为entityToDao
,类型为Map<Class<?>, AbstractDao<?, ?>>
的HashMap
,用于注册实体类Entity
和Dao
之间的关系
- 通过Dao类型,获取DaoConfig
- 根据type类型,初始化IdentityScope
- 根据DaoConfig,创建Dao实例
- 注册Dao,将实体类Entity和Dao映射起来
IdentityScope有2种类型,分别是IdentityScopeObject
和IdentityScopeLong
,它们的作用是根据主键缓存实体类数据,IdentityScopeLong
为主键为数字类型时使用,例如int、long等,而IdentityScopeObject
则为非数字类型的主键时使用,例如String
public class DaoSession extends AbstractDaoSession {
private final DaoConfig personInfoEntityDaoConfig;
private final PersonInfoEntityDao personInfoEntityDao;
public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
daoConfigMap) {
//调用父类的构造方法
super(db);
//1、通过Dao类型,获取DaoConfig
personInfoEntityDaoConfig = daoConfigMap.get(PersonInfoEntityDao.class).clone();
//2、根据type类型,初始化IdentityScope
personInfoEntityDaoConfig.initIdentityScope(type);
//3、根据DaoConfig,创建Dao实例
personInfoEntityDao = new PersonInfoEntityDao(personInfoEntityDaoConfig, this);
//4、注册Dao,将实体类Entity和Dao映射起来
registerDao(PersonInfoEntity.class, personInfoEntityDao);
}
}
//父类AbstractDaoSession
public class AbstractDaoSession {
private final Database db;
private final Map<Class<?>, AbstractDao<?, ?>> entityToDao;
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);
}
//...
}
插入流程分析
插入一条数据,只需要获取Dao实例,调insert()
方法,传入实体对象就可以了
//获取Dao对象
PersonInfoEntityDao dao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao();
//增
dao.insert(entity);
点进insert()
方法,发现是Dao的父类AbstractDao
的方法,并且里面定义了通用的CRUD方法
public abstract class AbstractDao<T, K> {
//增
public long insert(T entity) {
return executeInsert(entity, statements.getInsertStatement(), true);
}
//删
public void delete(T entity) {
assertSinglePk();
K key = getKeyVerified(entity);
deleteByKey(key);
}
//查
public List<T> loadAll() {
Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
return loadAllAndCloseCursor(cursor);
}
//改
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();
}
}
}
}
先来看看insert()
方法的流程,在AbstractDao
的构造方法中,传入了DaoConfig
,它是Dao的配置信息,通过它获取了TableStatements
对象,它是一个用于生成CRUD的SQL语句的工具类
- 插入的SQL语句,在通过
TableStatements
中的SqlUtils.createSqlInsert()
来生成 - 根据不同类型的数据库,将SQL语句编译成对应的语句
public abstract class AbstractDao<T, K> {
//...
protected final TableStatements statements;
@SuppressWarnings("unchecked")
public AbstractDao(DaoConfig config, AbstractDaoSession daoSession) {
this.config = config;
this.session = daoSession;
db = config.db;
isStandardSQLite = db.getRawDatabase() instanceof SQLiteDatabase;
identityScope = (IdentityScope<K, T>) config.getIdentityScope();
if (identityScope instanceof IdentityScopeLong) {
identityScopeLong = (IdentityScopeLong<T>) identityScope;
} else {
identityScopeLong = null;
}
//--------------------- 重点 ---------------------
statements = config.statements;
//--------------------- 重点 ---------------------
pkOrdinal = config.pkProperty != null ? config.pkProperty.ordinal : -1;
}
public long insert(T entity) {
return executeInsert(entity, statements.getInsertStatement(), true);
}
}
public class TableStatements {
//...
public DatabaseStatement getInsertStatement() {
if (insertStatement == null) {
//--------------------- 重点 ---------------------
String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns);
//--------------------- 重点 ---------------------
//编译对应数据库的Statement
DatabaseStatement newInsertStatement = db.compileStatement(sql);
synchronized (this) {
if (insertStatement == null) {
insertStatement = newInsertStatement;
}
}
if (insertStatement != newInsertStatement) {
newInsertStatement.close();
}
}
return insertStatement;
}
//...
}
继续看executeInsert()
方法
- 判断数据库是否被当前线程锁定,如是,则直接插入数据
- 如果没有被锁定,为了避免死锁,开启一个事务,再进行插入
- 最后,如果有设置主键,则把数据实体缓存到identityScope中
public abstract class AbstractDao<T, K> {
private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
long rowId;
//1、判断数据库是否被当前线程锁定,如是,则直接插入数据
if (db.isDbLockedByCurrentThread()) {
rowId = insertInsideTx(entity, stmt);
} else {
// Do TX to acquire a connection before locking the stmt to avoid deadlocks
//2、如果没有被锁定,为了避免死锁,开启一个事务,再进行插入
db.beginTransaction();
try {
rowId = insertInsideTx(entity, stmt);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
//3、最后,如果有设置主键,则把数据实体缓存到identityScope中
if (setKeyAndAttach) {
updateKeyAfterInsertAndAttach(entity, rowId, true);
}
return rowId;
}
}
上面继续调用了insertInsideTx()
方法
- 同步锁,保证线程安全
- 如果是标准数据库,使用stmt获取DatabaseStatement,再将实体的字段,绑定到语句中,执行插入SQL语句
- 如果是加密型数据库,直接把实体的字段,绑定到这个stmt,执行插入SQL语句
public abstract class AbstractDao<T, K> {
private long insertInsideTx(T entity, DatabaseStatement stmt) {
//同步锁,保证线程安全
synchronized (stmt) {
if (isStandardSQLite) {
//如果是标准数据库,使用stmt获取DatabaseStatement
SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
//再将实体的字段,绑定到语句中
bindValues(rawStmt, entity);
//执行插入SQL语句
return rawStmt.executeInsert();
} else {
//如果是加密型数据库,直接把实体的字段,绑定到这个stmt
bindValues(stmt, entity);
//执行插入SQL语句
return stmt.executeInsert();
}
}
}
}
bindValues()
方法,是一个抽象方法,在父类AbstractDao
上声明,Dao子类进行复写实现,例如这里是PersonInfoEntityDao
public class PersonInfoEntityDao extends AbstractDao<PersonInfoEntity, String> {
@Override
protected final void bindValues(DatabaseStatement stmt, PersonInfoEntity entity) {
stmt.clearBindings();
String id = entity.getId();
if (id != null) {
stmt.bindString(1, id);
}
stmt.bindLong(2, entity.getCreateTime().getTime());
stmt.bindLong(3, entity.getUpdateTime().getTime());
stmt.bindLong(4, entity.getDeleteFlag());
stmt.bindLong(5, entity.getVersion());
stmt.bindString(6, entity.getPersonName());
stmt.bindString(7, entity.getSex());
stmt.bindLong(8, entity.getAge());
}
@Override
protected final void bindValues(SQLiteStatement stmt, PersonInfoEntity entity) {
stmt.clearBindings();
String id = entity.getId();
if (id != null) {
stmt.bindString(1, id);
}
stmt.bindLong(2, entity.getCreateTime().getTime());
stmt.bindLong(3, entity.getUpdateTime().getTime());
stmt.bindLong(4, entity.getDeleteFlag());
stmt.bindLong(5, entity.getVersion());
stmt.bindString(6, entity.getPersonName());
stmt.bindString(7, entity.getSex());
stmt.bindLong(8, entity.getAge());
}
}
查询流程分析
//获取Dao对象
PersonInfoEntityDao dao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao();
//查
List<PersonInfoEntity> list = dao.loadAll();
loadAll()
方法,同样在父类AbstractDao
上,和插入一样,通过statements
对象,创建查询SQL语句,执行语句,拿到结果Cursor游标,loadAll()
方法接着调用loadAllFromCursor()
loadAllFromCursor()
,通过游标Cursor来遍历数据
- 判断游标cursor,是否是一个跨进程的CrossProcessCursor
- 并且,cursor的行数没有偏差的话,则创建一个加快版的FastCursor进行游标遍历
-
loadCurrent()
,遍历游标,并把数据组装到实体类
public abstract class AbstractDao<T, K> {
protected final TableStatements statements;
public AbstractDao(DaoConfig config, AbstractDaoSession daoSession) {
this.config = config;
this.session = daoSession;
db = config.db;
isStandardSQLite = db.getRawDatabase() instanceof SQLiteDatabase;
identityScope = (IdentityScope<K, T>) config.getIdentityScope();
if (identityScope instanceof IdentityScopeLong) {
identityScopeLong = (IdentityScopeLong<T>) identityScope;
} else {
identityScopeLong = null;
}
//--------------------- 重点 ---------------------
statements = config.statements;
//--------------------- 重点 ---------------------
pkOrdinal = config.pkProperty != null ? config.pkProperty.ordinal : -1;
}
//查询所有
public List<T> loadAll() {
Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
return loadAllAndCloseCursor(cursor);
}
protected List<T> loadAllAndCloseCursor(Cursor cursor) {
try {
return loadAllFromCursor(cursor);
} finally {
cursor.close();
}
}
protected List<T> loadAllFromCursor(Cursor cursor) {
int count = cursor.getCount();
if (count == 0) {
return new ArrayList<T>();
}
List<T> list = new ArrayList<T>(count);
CursorWindow window = null;
boolean useFastCursor = false;
//判断游标cursor,是否是一个跨进程的CrossProcessCursor
if (cursor instanceof CrossProcessCursor) {
window = ((CrossProcessCursor) cursor).getWindow();
if (window != null) { // E.g. Robolectric has no Window at this point
//并且,cursor的行数没有偏差的话,则创建一个加快版的FastCursor进行游标遍历
if (window.getNumRows() == count) {
cursor = new FastCursor(window);
useFastCursor = true;
} else {
DaoLog.d("Window vs. result size: " + window.getNumRows() + "/" + count);
}
}
}
if (cursor.moveToFirst()) {
if (identityScope != null) {
identityScope.lock();
identityScope.reserveRoom(count);
}
try {
if (!useFastCursor && window != null && identityScope != null) {
loadAllUnlockOnWindowBounds(cursor, window, list);
} else {
do {
//遍历游标,并把数据组装到实体类
list.add(loadCurrent(cursor, 0, false));
} while (cursor.moveToNext());
}
} finally {
if (identityScope != null) {
identityScope.unlock();
}
}
}
return list;
}
}
loadCurrent
,把游标的数据组装到对应的实体类中
- 先从缓存中获取实体数据,根据主键的类型,如果是数字类型,从
identityScopeLong
缓存中获取,非数字类型,则从identityScope
中获取 - 如果获取到了,直接返回,没有获取到,则创建一个实体
- 再把实体缓存起来,返回实体
final protected T loadCurrent(Cursor cursor, int offset, boolean lock) {
//主键为数据类型
if (identityScopeLong != null) {
if (offset != 0) {
// Occurs with deep loads (left outer joins)
if (cursor.isNull(pkOrdinal + offset)) {
return null;
}
}
//生成缓存Key
long key = cursor.getLong(pkOrdinal + offset);
//先从缓存identityScopeLong中获取数据实体
T entity = lock ? identityScopeLong.get2(key) : identityScopeLong.get2NoLock(key);
//如果能获取到缓存实体,直接返回去
if (entity != null) {
return entity;
} else {
//获取不到,创建一个数据实体
entity = readEntity(cursor, offset);
attachEntity(entity);
//把实体,添加进缓存
if (lock) {
identityScopeLong.put2(key, entity);
} else {
identityScopeLong.put2NoLock(key, entity);
}
return entity;
}
} else if (identityScope != null) {
//主键非数字类型,例如字符串
K key = readKey(cursor, offset);
if (offset != 0 && key == null) {
// Occurs with deep loads (left outer joins)
return null;
}
//同样,先从缓存中获取
T entity = lock ? identityScope.get(key) : identityScope.getNoLock(key);
if (entity != null) {
//获取到了,直接返回
return entity;
} else {
//获取不到,创建一个实体
entity = readEntity(cursor, offset);
//缓存实体到identityScope
attachEntity(key, entity, lock);
return entity;
}
} else {
// Check offset, assume a value !=0 indicating a potential outer join, so check PK
if (offset != 0) {
K key = readKey(cursor, offset);
if (key == null) {
// Occurs with deep loads (left outer joins)
return null;
}
}
T entity = readEntity(cursor, offset);
attachEntity(entity);
return entity;
}
}
readEntity()
,通过Cursor游标,取出数据并设置到一个新的实体上,再返回。这个方法在父类AbstractDao
中定义,子类中复写进行实现
//Dao父类
public abstract class AbstractDao<T, K> {
abstract protected T readEntity(Cursor cursor, int offset);
}
//Dao子类
public class PersonInfoEntityDao extends AbstractDao<PersonInfoEntity, String> {
@Override
public PersonInfoEntity readEntity(Cursor cursor, int offset) {
PersonInfoEntity entity = new PersonInfoEntity( //
cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // id
new java.util.Date(cursor.getLong(offset + 1)), // createTime
new java.util.Date(cursor.getLong(offset + 2)), // updateTime
cursor.getInt(offset + 3), // deleteFlag
cursor.getInt(offset + 4), // version
cursor.getString(offset + 5), // personName
cursor.getString(offset + 6), // sex
cursor.getInt(offset + 7) // age
);
return entity;
}
}
由于GreenDao在CRUD时,会缓存实体数据,所以如果通过其他数据库框架,或者手写SQL对表进行修改,则会导致GreenDao有脏数据,这时通过DaoSession
的clear()
方法,清空缓存即可
public class DaoSession extends AbstractDaoSession {
//...
public void clear() {
personInfoEntityDaoConfig.clearIdentityScope();
}
//...
}
GreenDao和RxJava1结合
GreenDao提供了与RxJava1结合的操作方法,来看看怎么使用
//获取Dao对象
RxDao<PersonInfoEntity, String> rxDao = GreenDaoManager.getInstance().getDaoSession().getPersonInfoEntityDao().rx();
//增
dao.insert(entity)
.observerOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<PersonInfoEntity>() {
@Override
public void call(PersonInfoEntity entity) {
//插入成功
}
});
AbstractDao,rx()
创建RxDao
对象,传入了Schedulers.io()
IO调度器
- 通过
wrap()
方法,调用fromCallable()
,把Callable
回调包装成Observable
返回 - 如果RxDao创建时,有调度器,则进行线程切换,在订阅时在指定的线程中执行,例如 Schedulers.io(),就在IO线程
-
Callable
回调中,依旧是使用Dao层的insert
方法进行插入 -
Observable.defer()
,defer()
操作符,是确保Observable
被订阅后,才执行Callable
回调,进行数据库操作
public abstract class AbstractDao<T, K> {
//不指定调度器,需要自己指定`subscribeOn`操作符在哪个调度器中执行数据库操作
@Experimental
public RxDao<T, K> rxPlain() {
if (rxDaoPlain == null) {
rxDaoPlain = new RxDao<>(this);
}
return rxDaoPlain;
}
@Experimental
public RxDao<T, K> rx() {
//指定调度器为Schedulers.io()
if (rxDao == null) {
rxDao = new RxDao<>(this, Schedulers.io());
}
return rxDao;
}
}
@Experimental
public class RxDao<T, K> extends RxBase {
//插入
@Experimental
public Observable<T> insert(final T entity) {
return wrap(new Callable<T>() {
@Override
public T call() throws Exception {
//调用Dao的插入
dao.insert(entity);
return entity;
}
});
}
//包装Callable为Observable
protected <R> Observable<R> wrap(Callable<R> callable) {
return wrap(RxUtils.fromCallable(callable));
}
protected <R> Observable<R> wrap(Observable<R> observable) {
//如果RxDao创建时,有调度器,则进行线程切换,在订阅时在指定的线程中执行,例如 Schedulers.io(),就在IO线程
if (scheduler != null) {
return observable.subscribeOn(scheduler);
} else {
return observable;
}
}
}
@Internal
class RxUtils {
/** As of RxJava 1.1.7, Observable.fromCallable is still @Beta, so just in case... */
@Internal
static <T> Observable<T> fromCallable(final Callable<T> callable) {
//defer()操作符,是确保Observable被订阅后,才执行Callable回调,进行数据库操作
return Observable.defer(new Func0<Observable<T>>() {
@Override
public Observable<T> call() {
T result;
try {
result = callable.call();
} catch (Exception e) {
return Observable.error(e);
}
return Observable.just(result);
}
});
}
}
网友评论