在进行项目开发时,免不了要实现存储功能,也就是我们常说的数据持久化,我们知道,android五种存储方式,网络存储,数据库存储,文件存储,SharePreference和contentprovider,其中数据库存储是比较常用的一种方式,但是android提供的原生的sqlite用起来比较麻烦,还需要自己进行封装,所以今天就来讲一讲greendao;
greenDAO是一种Android数据库ORM(object/relational mapping)框架,与OrmLite、ActiveOrm、LitePal等数据库相比,单位时间内可以插入、更新和查询更多的数据,而且提供了大量的灵活通用接口。
1.使用方法:
(1)配置环境(Android Studio):
(A)首先将工程目录下的build.gradle文件配置成
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
主要是在repositories加入mavenCentral(),再在dependencies中加入依赖;
(B)再将app目录下的build.gradle的文件配置成
先加入greendao的插件:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'
再加入依赖
compile'org.greenrobot:greendao-generator:3.1.0'
最后再在android下配置
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
greendao{
schemaVersion 1
daoPackage'com.example.mytest.greendao.gen'
targetGenDir'src/main/java'
}
schemaVersion---->指定数据库schema版本号,迁移等操作会用到
daoPackage-------->通过gradle插件生成的数据库相关文件,这里我设置的文件路径是com.example.anonymous.greendao
targetGenDir-------->这就是我们上面说到的自定义生成数据库文件的目录了,可以将生成的文件放到我们的java目录中,而 不是build中,这样就不用额外的设置资源目录了
配置好这些之后,同步一下;
接下来就是代码的编写了:
首先编写实体类
@Entity
public class User {
@Id
private String id;
private String name;
}
这时点击makeProject,文件目录就会发生变化;

首先User类会自动生成get,set方法
@Entity
public class User {
@Id
private String id;
private String name;
@Generated(hash = 1037321026)
public User(String id, String name) {
this.id = id;
this.name = name;
}
@Generated(hash = 586692638)
public User() {
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
然后会在你之前配置的targetgenDir目录下,生成DaoMaster,DaoSession和UserDao;

DaoManager是我自己封装的工具类,这里的DaoMaster是数据库的“管家”,主要功能是负责数据库的创建和删除,并将数据库存储到Session缓存中,DaoSession提供了方便的方法去让你操作数据库,并且是数据库的session的缓存,UserDao是根据User实体类来生成的对实体类的操作的具体实现类;
有了这些类,我们就可以方便的去操作数据库了;
(2)具体操作:
首先编写一个dao的管理类,初始化数据库,获取相应的操作对象
public class DaoManager {
private static DaoManager mInstance;
private DaoMaster daoMaster;
private DaoSession daoSession;
private DaoManager(){
DaoMaster.DevOpenHelper devOpenHelper=new DaoMaster.DevOpenHelper(MyApplication.getContext(),"my_db",null);
daoMaster=new DaoMaster(devOpenHelper.getWritableDatabase());
daoSession=daoMaster.newSession();
}
public DaoMaster getDaoMaster(){
return daoMaster;
}
public DaoSession getDaoSession(){
return daoSession;
}
public static DaoManager getmInstance(){
if (mInstance==null){
mInstance=new DaoManager();
}
return mInstance;
}
}
再编写实体类的管理类,对数据库表的初始化,获取实体类的操作对象
public class EntityManager {
private static EntityManager manager;
private UserDao userDao;
public UserDao getUserDao(){
return DaoManager.getmInstance().getDaoSession().getUserDao();
}
public static EntityManager getManager(){
if (manager==null){
manager=new EntityManager();
}
return manager;
}
}
代码测试:增删改查
//增加
private void insertData() {
userDao.insert(new User("001","pgg"));
}
//删除
private void deleteData() {
User pgg = userDao.queryBuilder().where(UserDao.Properties.Name.eq("pgg")).build().unique();
if (pgg!=null){
userDao.deleteByKey(pgg.getId());
}
}
//更新
private void updateData() {
User pgg = userDao.queryBuilder().where(UserDao.Properties.Name.eq("pgg")).build().unique();
if (pgg!=null){
pgg.setName("pggpg");
userDao.update(pgg);
Toast.makeText(MyApplication.getContext(), "修改成功", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MyApplication.getContext(), "修改失败", Toast.LENGTH_SHORT).show();
}
}
//查找
private void queryData() {
User load = userDao.load("001");
tv_id.setText("id="+load.getId());
tv_name.setText("name="+load.getName());
}
这里只不过使用了greendao的几种方法,增删改查还有很多的方法,需要的可以在官网上查询;
2.源码分析
greendao的源码我准备从他的四个核心类去分析,DaoMaster,DaoSession,XXXDao,XXXEntity;
(1)DaoMaster:DaoMaster是GreenDao框架管理类,该类对数据库相关管理操作进行封装
从DaoManager类中就可以看出,使用greendao的入口就是创建DevOpenHelper类
DaoMaster.DevOpenHelper devOpenHelper=new DaoMaster.DevOpenHelper(MyApplication.getContext(),"my_db",null);
这个DevOpenHelper是个什么东西呢?既然DevOpenHelper是DaoMaster的静态内部类,那么我们就看看DaoMaster的源码
/**
* 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除了具有创建表和删除表的两个功能外,还有两个内部类,分别为OpenHelper和DevOpenHelper,OpenHelper继承于SQLiteOpenHelper用于创建所有的数据库表;DevOpenHelper继承于OpenHelper用于数据库升级,而重写的onCreated()方法调用了createAllTables(db,false);方法来创建数据表,而createAllTables()方法是通过调用UserDao静态方法来创建表的;
/** Creates underlying database table using DAOs. */
public static void createAllTables(Database db, boolean ifNotExists) {
UserDao.createTable(db, ifNotExists);
}
我们来看看UserDao的createTable方法
public static void createTable(Database db, boolean ifNotExists) {
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\"USER\" (" + //
"\"ID\" TEXT PRIMARY KEY NOT NULL ," + // 0: id
"\"NAME\" TEXT);"); // 1: name
}
可以很清晰的看到,这里面是通过sql语句来创建表的,只不过这些sql语句不需要我们敲,greendao已经帮我们封装好了,其实删除也是一样
/** Drops the underlying database table. */
public static void dropTable(Database db, boolean ifExists) {
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"USER\"";
db.execSQL(sql);
}
我们现在知道是怎么创建和删除表的了,同时,这里还需要注意,这里的DevOpenHelper实现了onUpgrade方法,用于数据库的版本升级,在里面首先它会将所有的表全部删除后重新创建,这样以前的数据就不会存在,所以,我们如果想在版本升级的时候,需要自己封装方法将所有的数据全部导入到新表中;
有了DevOpenHelper类后,接下来再看DaoMaster的newSession方法
daoSession=daoMaster.newSession();
newSession方法其实就是创建了一个DaoSession实例,关于DaoSession的实例,greendao官方建议不要重新创建新的实例,保持单例即可;DaoSession我们在后面分析,先继续看DaoMaster
现在看看DaoMaster的父类AbstractDaoMaster:
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);}这是一个抽象类,里面定义了一些方法,其中在它的构造方法中,有这么一句代码:
daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
这里定义了一个map集合,其中的key是继承自AbstractDao的字节码对象,value则是DaoConfig
public DaoConfig(Database db, Class<? extends AbstractDao<?, ?>> daoClass) {
this.db = db;
try {
this.tablename = (String) daoClass.getField("TABLENAME").get(null);
Property[] properties = reflectProperties(daoClass);
this.properties = properties;
allColumns = new String[properties.length];
List<String> pkColumnList = new ArrayList<String>();
List<String> nonPkColumnList = new ArrayList<String>();
Property lastPkProperty = null;
for (int i = 0; i < properties.length; i++) {
Property property = properties[i];
String name = property.columnName;
allColumns[i] = name;
if (property.primaryKey) {
pkColumnList.add(name);
lastPkProperty = property;
} else {
nonPkColumnList.add(name);
}
}
String[] nonPkColumnsArray = new String[nonPkColumnList.size()];
nonPkColumns = nonPkColumnList.toArray(nonPkColumnsArray);
String[] pkColumnsArray = new String[pkColumnList.size()];
pkColumns = pkColumnList.toArray(pkColumnsArray);
pkProperty = pkColumns.length == 1 ? lastPkProperty : null;
statements = new TableStatements(db, tablename, allColumns, pkColumns);
if (pkProperty != null) {
Class<?> type = pkProperty.type;
keyIsNumeric = type.equals(long.class) || type.equals(Long.class) || type.equals(int.class)
|| type.equals(Integer.class) || type.equals(short.class) || type.equals(Short.class)
|| type.equals(byte.class) || type.equals(Byte.class);
} else {
keyIsNumeric = false;
}
} catch (Exception e) {
throw new DaoException("Could not init DAOConfig", e);
}
}
每个AbstractDao对应一个DaoConfig
protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
DaoConfig daoConfig = new DaoConfig(db, daoClass);
daoConfigMap.put(daoClass, daoConfig);
}
这个DaoConfig内部使用的反射,将表中的字段映射到Property数组中;
所以现在的Map的作用就很清晰了,就是为每一个EntityDao对象的字节码建立与之对应的db数据库的映射关系,从而管理所有的EntityDao类,同时我们发现registerDaoClass这个将EntityDao与数据库绑定的方法是在DaoMaster的构造方法调用的,所以,我们在创建DaoMaster的时候,同时就做好了绑定;
接下来看DaoSession类:
(2)DaoSession
从上面可知DaoSession对象是通过master.newSession();创建的。DaoSession对象是连接GreenDao框架到SQLite数据库的纽带,通过该对象我们可以得到一个与数据库某个表相关的操作对象xxxDao。我们看看DaoSession源码,发现它也有一个抽象的父类AbstractDaoSession,我们来看看DaoSession的源码:
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的源码很少,主要就是为了得到UserDao的对象,并且提供方法给外界访问,其中有这么一行
userDaoConfig = daoConfigMap.get(UserDao.class).clone();
这个正是从在DaoMaster创建的Map集合中取出key为UserDao.class的DaoConfig对象,刚刚就说了Map集合中保存了UserDao类对应的数据库db的关系映射,而这个DaoConfig对象正是管理了对应的db对象。然后把这个DaoConfig传给UserDao(UserDaoConfig, this),所以这就说明了我们使用UserDao对象来进行数据库上的CRUD操作而对应的数据库也会变化的原因,这个过程实际上就是在间接操作数据库。
接下来就看看它的父类AbstractDaoSession,这个类的源码较长,不过大多都是对数据库的增删改查,所以重点看它的这些方法:
/** 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);
}
/** Convenient call for {@link AbstractDao#update(Object)}. */
public <T> void update(T entity) {
@SuppressWarnings("unchecked")
AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
dao.update(entity);
}
/** Convenient call for {@link AbstractDao#delete(Object)}. */
public <T> void delete(T entity) {
@SuppressWarnings("unchecked")
AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
dao.delete(entity);
}
/** Convenient call for {@link AbstractDao#deleteAll()}. */
public <T> void deleteAll(Class<T> entityClass) {
@SuppressWarnings("unchecked")
AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entityClass);
dao.deleteAll();
}
/** Convenient call for {@link AbstractDao#load(Object)}. */
public <T, K> T load(Class<T> entityClass, K key) {
@SuppressWarnings("unchecked")
AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
return dao.load(key);
}
可以很清晰的看到,DaoSession的增删改查都是调用UserDao中的增删改查的方法,通过上面的分析我们知道事实上,它的内部就是使用UserDao进行增删改查操作;
到此,我们只看到了DaoSession源码表面上的功能,这些功能就是它管理了指定模式下所有可用的DAO对象,并且提供了getter方法供我们得到这些DAO对象,它还提供了一些CRUD方法。实际上DaoSession和StudentDao在调用CRUD的方法进行CRUD操作时,其中的查询操作就是最特别的,为什么呢?原因是GreenDao在查询这块加了缓存;
GreenDao在查询时使用了弱引用WeakReference,假如第一次查询时候我查询了小明这个Student的数据,那么它将把小明加入一个SparseArray>的集合中,下次如果再次查询小明这个学生的时候,将立即会返回这个引用从而不必再查询数据库(前提是GC还没回收这些引用)。
这个缓存的代码是在AbstractQueryData类中,如下:
final
Map<
long
, q=
""
>> queriesForThreads;
Q forCurrentThread(Q query) {
if
(Thread.currentThread() == query.ownerThread) {
System.arraycopy(initialValues,
0
, query.parameters,
0
, initialValues.length);
return
query;
}
else
{
return
forCurrentThread();
}
}
Q forCurrentThread() {
long
threadId = Thread.currentThread().getId();
synchronized
(queriesForThreads) {
WeakReference<q> queryRef = queriesForThreads.get(threadId);
Q query = queryRef !=
null
? queryRef.get() :
null
;
if
(query ==
null
) {
gc();
query = createQuery();
queriesForThreads.put(threadId,
new
WeakReference<q>(query));
}
else
{
System.arraycopy(initialValues,
0
, query.parameters,
0
, initialValues.length);
}
return
query;
}
}
(3)Entity实体类
对于实体类,这没什么可讲的,就是一个Bean,一个实体类对应一张表,实体类里面有对应各个字段的getter和setter方法
(4)EntityDao实体Dao类
由生成器生成的数据库操作类,它继承于AbstractDao,封装了所有对数据库表进行增删改成的方法。可以这么说,我们之所以使用GreenDao管理本地数据库无需与SQL语句打交道,就是因为GreenDao框架在这一层已经对大部分数据库操作SQL语句进行了封装。
public class UserDao extends AbstractDao<User, String> {
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, String.class, "id", true, "ID");
public final static Property Name = new Property(1, String.class, "name", false, "NAME");
}
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\" TEXT PRIMARY KEY NOT NULL ," + // 0: id
"\"NAME\" TEXT);"); // 1: name
}
/** 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();
String id = entity.getId();
if (id != null) {
stmt.bindString(1, id);
}
String name = entity.getName();
if (name != null) {
stmt.bindString(2, name);
}
}
@Override
protected final void bindValues(SQLiteStatement stmt, User entity) {
stmt.clearBindings();
String id = entity.getId();
if (id != null) {
stmt.bindString(1, id);
}
String name = entity.getName();
if (name != null) {
stmt.bindString(2, name);
}
}
@Override
public String readKey(Cursor cursor, int offset) {
return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0);
}
@Override
public User readEntity(Cursor cursor, int offset) {
User entity = new User( //
cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // id
cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1) // name
);
return entity;
}
@Override
public void readEntity(Cursor cursor, User entity, int offset) {
entity.setId(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0));
entity.setName(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1));
}
@Override
protected final String updateKeyAfterInsert(User entity, long rowId) {
return entity.getId();
}
@Override
public String getKey(User entity) {
if(entity != null) {
return entity.getId();
} else {
return null;
}
}
@Override
public boolean hasKey(User entity) {
return entity.getId() != null;
}
@Override
protected final boolean isEntityUpdateable() {
return true;
}
}
其中bindValues()这个方法就是绑定实体的属性名和表中的字段名的,还有比较重要的就是这个静态内部类Properties,该类中分别对每一个实体类的属性都创建了一个Property对象,而我们可以根据Property来得到这个属性对应表中的列名、是否为主键等值,其中还包括了一些方法,比如判断表中某个字段的值是否和value相等:eq(Object value);
而AbstractDao源码中主要是一些CRUD方法和其它的一些方法,源码太长了,避免影响篇幅,我删了一部分:
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 AbstractDaoSession getSession() {
return session;
}
TableStatements getStatements() {
return config.statements;
}
public String getTablename() {
return config.tablename;
}
public Property[] getProperties() {
return config.properties;
}
public Property getPkProperty() {
return config.pkProperty;
}
public String[] getAllColumns() {
return config.allColumns;
}
public String[] getPkColumns() {
return config.pkColumns;
}
public String[] getNonPkColumns() {
return config.nonPkColumns;
}
/**
* Loads the entity for the given PK.
*
* @param key a PK value or null
* @return The entity or null, if no entity matched the PK value
*/
public T load(K key) {
assertSinglePk();
if (key == null) {
return null;
}
if (identityScope != null) {
T entity = identityScope.get(key);
if (entity != null) {
return entity;
}
}
String sql = statements.getSelectByKey();
String[] keyArray = new String[]{key.toString()};
Cursor cursor = db.rawQuery(sql, keyArray);
return loadUniqueAndCloseCursor(cursor);
}
public T loadByRowId(long rowId) {
String[] idArray = new String[]{Long.toString(rowId)};
Cursor cursor = db.rawQuery(statements.getSelectByRowId(), idArray);
return loadUniqueAndCloseCursor(cursor);
}
protected T loadUniqueAndCloseCursor(Cursor cursor) {
try {
return loadUnique(cursor);
} finally {
cursor.close();
}
}
protected T loadUnique(Cursor cursor) {
boolean available = cursor.moveToFirst();
if (!available) {
return null;
} else if (!cursor.isLast()) {
throw new DaoException("Expected unique result, but count was " + cursor.getCount());
}
return loadCurrent(cursor, 0, true);
}
/** Loads all available entities from the database. */
public List<T> loadAll() {
Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
return loadAllAndCloseCursor(cursor);
}
/** Detaches an entity from the identity scope (session). Subsequent query results won't return this object. */
public boolean detach(T entity) {
if (identityScope != null) {
K key = getKeyVerified(entity);
return identityScope.detach(key, entity);
} else {
return false;
}
}
/**
* Detaches all entities (of type T) from the identity scope (session). Subsequent query results won't return any
* previously loaded objects.
*/
public void detachAll() {
if (identityScope != null) {
identityScope.clear();
}
}
protected List<T> loadAllAndCloseCursor(Cursor cursor) {
try {
return loadAllFromCursor(cursor);
} finally {
cursor.close();
}
}
四大核心类以及介绍完,接下来讲一讲greendao是如何进行缓存的:
主要通过DaoConfig的initIdentityScope方法:
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);
}
}
可见它是由IdentityScope接口来实现的,它有两个实现类:IdentityScopeLong类和IdentityScopeObject类,下面看看IdentityScopeObject类:
public IdentityScopeObject() {
map = new HashMap<K, Reference<T>>();
lock = new ReentrantLock();
}
在执行插入操作时,会进行缓存,我们从AbstractDao类中代码可知:
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;
}
给操作的bean更新id
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)");
}
}
缓存操作:IdentityScope键值对,key为表中主键,value为bean
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);
}
}
}
总结一下:
GreenDao的逻辑:
1. 实例化一个SQLiteOpenHelper对象,以便建立与指定数据库(如”testDb”)之间的连接;
2. 调用SQLiteOpenHelper的getWritableDatabase()方法以读写方式打开所连接的数据库;
3. 通过获得的数据库对象SQLiteDatabase来创建GreenDao框架管理者DaoMaster对象;
4. 调用DaoMaster的newSession()方法实例化一个数据库会话对象DaoSession;
5. 通过DaoSession对象获得最终能够操作数据库表的xxxxDao对象