GreenDao3使用完全解析
1,gradle配置(官网样例地址https://github.com/greenrobot/greenDAO/blob/master/examples/RxDaoExample/build.gradle) Module的gradle 里安装如下配置(官方给的配置样例) buildscript { repositories { jcenter() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:2.3.1' classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' } } apply plugin: 'com.android.application' apply plugin: 'org.greenrobot.greendao' android { compileSdkVersion 25 buildToolsVersion "25.0.3" defaultConfig { applicationId "com.example.administrator.myapplication" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } greendao { targetGenDir 'src/main/java' daoPackage 'com.example.administrator.myapplication' /* *schemaVersion 当前数据库结构的版本。结构版本变化时在OpenHelpers中被使用到。当你改变实体或者数据的结构时,这个值应该增加。 daoPackage 生成的DAO,DaoMaster和DaoSession的包名。默认是实体的包名。 targetGenDir 生成源文件的路径。默认源文件目录是在build目录中的(build/generated/source/greendao)。 generateTests 设置是否自动生成单元测试。 targetGenDirTest 生成的单元测试的根目录。 * */ } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.3.1' compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' compile 'org.greenrobot:greendao:3.2.2' compile 'org.greenrobot:greendao-generator:3.2.2' } uploadArchives.enabled = false 2,GreenDao注解详解 /* * A、 @Entity用于描述实体类名,其中active表示update/delete/refresh 方法是否自动生成,默认为false. createInDb表示是否在数据库中创建表,默认为true,如果为false,将不创建该表. generateConstructors表示是否自动生成构造方法(一个有参构造,一个无参构造). indexes表示制定查询数据返回的默认排序规则.(@Index中的value制定排序的数据表中的列明加上排序规则即(ASC/DESC), name表示......,unique表示是否唯一即SQL中的去重复 如果按照多个字段来排序可以这样(比如(indexes={@Index(value="ID ASC"),@Index(value="AGE DESC")}或者 indexes={@Index(value="ID ASC AGE DESC")}))) nameInDb表示该实体对应的数据表的名称,默认为实体名的拼音全大写 generateGettersSetters表示是否自动生成get/set方法,默认为true B、@Id表示该字段是主键,autoincrement表示是否自增,默认为false. C、@Property用于描述字段,nameInDb表示该字段在数据表中对应的列名,默认是实体字段名称. D、@NotNull表示该字段不为null. E、@Transient 表示在创建数据表时候忽略这个字段,也就是在创建表的时候不会创建这个字段. F、@ToOne表示一对一的关系,也就是多条这个实体只对应一条标识的实体joinProperty标识这个实体表中的哪个字段和标识的实体表的主键关联. G、@ToMany标识一对多的关系,也就是一条该实体数据通过指定列名和标识的数据实体的指定列名对应关系(@referencedJoinProperty表示当前标识的实体对应的数据表的 主键,@joinProperties表示当前表和标识的实体对应的数据表的属性对应关系) H、@Convert定义当前标识的实体和数据表中字段之间装换规则.converter表示转换器.columnType表示对应的数据表列名在表中的数据类型, * */ 以下是这些注释的详细例子: package com.example.administrator.myapplication; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.annotation.Convert; import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.JoinProperty; import org.greenrobot.greendao.annotation.NotNull; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Transient; import java.util.List; /** * Created by Administrator on 2017/5/26. */ @org.greenrobot.greendao.annotation.Entity( active = true//表示update/delete/refresh 方法是否自动生成,默认为false ,createInDb = true//表示是否在数据库中创建该表,默认为true ,generateConstructors = true//表示是否生成构造方法(一般有两个,一个有参数,一个无参数),默认为false ,indexes ={ @Index(value ="Id ASC"),@Index(value = "Age DESC")} //indexes表示数据表查询返回数据默认排序,name中的字段是该实体在数据表中的列名,value表示改实体的真实名称,unique表示是否唯一(默认为false) ,nameInDb = "STUDENT"//表示数据表的名称默认为实体类的名称 ,generateGettersSetters = true//表示是否生成get/set方法,默认为true ) public class Student { @org.greenrobot.greendao.annotation.Id(autoincrement = true)//设置数据表主键,autoincrement设置是否自增 @Property(nameInDb = "ID")//设置该字段在数据表中的名字,当字段设置为主键时候,那么该字段必须为Long型 public Long Id; @NotNull @Property(nameInDb = "AGE") public int Age; @NotNull @Property(nameInDb = "NAME") public String Name; @Transient//表示在创建表是否会忽略掉这个字段 public String TransientName; @org.greenrobot.greendao.annotation.ToOne(joinProperty = "Id")//设置多对一关系,即一条该实体数据对应多条ToOne数据的关系 public ToOne One; @org.greenrobot.greendao.annotation.ToMany(referencedJoinProperty = "Age",joinProperties ={ @JoinProperty( referencedName ="Age",name = "Age")//建立多表连接,多对多的关系,当前表的name表示的字段和referencedName //说表示的另外一个表的字段关联查询 }) public List<ToMany> mToManyList; @Property(nameInDb = "TEST") @Convert( converter = com.example.administrator.myapplication.Convert.class,columnType = String.class)//字段转换器,将实体数据装换为数据表中的数据 public Entity test; /** Used to resolve relations */ @Generated(hash = 2040040024) private transient DaoSession daoSession; /** Used for active entity operations. */ @Generated(hash = 1943931642) private transient StudentDao myDao; @Generated(hash = 510098685) public Student(Long Id, int Age, @NotNull String Name, Entity test) { this.Id = Id; this.Age = Age; this.Name = Name; this.test = test; } @Generated(hash = 1556870573) public Student() { } public Long getId() { return this.Id; } public void setId(Long Id) { this.Id = Id; } public int getAge() { return this.Age; } public void setAge(int Age) { this.Age = Age; } public String getName() { return this.Name; } public void setName(String Name) { this.Name = Name; } public com.example.administrator.myapplication.Entity getTest() { return this.test; } public void setTest(Entity test) { this.test = test; } @Generated(hash = 907378687) private transient Long One__resolvedKey; /** To-one relationship, resolved on first access. */ @Generated(hash = 1410041857) public ToOne getOne() { Long __key = this.Id; if (One__resolvedKey == null || !One__resolvedKey.equals(__key)) { final DaoSession daoSession = this.daoSession; if (daoSession == null) { throw new DaoException("Entity is detached from DAO context"); } ToOneDao targetDao = daoSession.getToOneDao(); ToOne OneNew = targetDao.load(__key); synchronized (this) { One = OneNew; One__resolvedKey = __key; } } return One; } /** called by internal mechanisms, do not call yourself. */ @Generated(hash = 2129737595) public void setOne(ToOne One) { synchronized (this) { this.One = One; Id = One == null ? null : One.getId(); One__resolvedKey = Id; } } /** * To-many relationship, resolved on first access (and after reset). * Changes to to-many relations are not persisted, make changes to the target entity. */ @Generated(hash = 2087991423) public List<ToMany> getMToManyList() { if (mToManyList == null) { final DaoSession daoSession = this.daoSession; if (daoSession == null) { throw new DaoException("Entity is detached from DAO context"); } ToManyDao targetDao = daoSession.getToManyDao(); List<ToMany> mToManyListNew = targetDao._queryStudent_MToManyList(Age); synchronized (this) { if (mToManyList == null) { mToManyList = mToManyListNew; } } } return mToManyList; } /** Resets a to-many relationship, making the next get call to query for a fresh result. */ @Generated(hash = 1358693666) public synchronized void resetMToManyList() { mToManyList = null; } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 128553479) public void delete() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.delete(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 1942392019) public void refresh() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.refresh(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 713229351) public void update() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.update(this); } /** called by internal mechanisms, do not call yourself. */ @Generated(hash = 1701634981) public void __setDaoSession(DaoSession daoSession) { this.daoSession = daoSession; myDao = daoSession != null ? daoSession.getStudentDao() : null; } } package com.example.administrator.myapplication; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.NotNull; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Unique; import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.DaoException; /** * Created by Administrator on 2017/5/26. */ @Entity( nameInDb = "TOMANY" ,indexes = {@Index(value = "Id ASC")} ,active = true) public class ToMany { @org.greenrobot.greendao.annotation.Id(autoincrement = true) @NotNull @Unique @Property(nameInDb = "ID") public Long Id; @Property(nameInDb = "NAME") @NotNull public String Name; @Property(nameInDb = "AGE") @NotNull public int Age; /** Used to resolve relations */ @Generated(hash = 2040040024) private transient DaoSession daoSession; /** Used for active entity operations. */ @Generated(hash = 1963614772) private transient ToManyDao myDao; @Generated(hash = 695029563) public ToMany(@NotNull Long Id, @NotNull String Name, int Age) { this.Id = Id; this.Name = Name; this.Age = Age; } @Generated(hash = 1463867877) public ToMany() { } public Long getId() { return this.Id; } public void setId(Long Id) { this.Id = Id; } public String getName() { return this.Name; } public void setName(String Name) { this.Name = Name; } public int getAge() { return this.Age; } public void setAge(int Age) { this.Age = Age; } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 128553479) public void delete() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.delete(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 1942392019) public void refresh() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.refresh(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 713229351) public void update() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.update(this); } /** called by internal mechanisms, do not call yourself. */ @Generated(hash = 1942134982) public void __setDaoSession(DaoSession daoSession) { this.daoSession = daoSession; myDao = daoSession != null ? daoSession.getToManyDao() : null; } } package com.example.administrator.myapplication; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.NotNull; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Unique; import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.DaoException; /** * Created by Administrator on 2017/5/26. */ @Entity( nameInDb = "TOONE" ,indexes = {@Index(value = "Id ASC")} ,active = true ) public class ToOne { @org.greenrobot.greendao.annotation.Id(autoincrement = true) @Property(nameInDb = "ID") @NotNull @Unique public Long Id; @Property(nameInDb = "NAME") @NotNull public String Name; @Property(nameInDb = "AGE") @NotNull public int Age; /** Used to resolve relations */ @Generated(hash = 2040040024) private transient DaoSession daoSession; /** Used for active entity operations. */ @Generated(hash = 705594293) private transient ToOneDao myDao; @Generated(hash = 1228318430) public ToOne(@NotNull Long Id, @NotNull String Name, int Age) { this.Id = Id; this.Name = Name; this.Age = Age; } @Generated(hash = 348073711) public ToOne() { } public Long getId() { return this.Id; } public void setId(Long Id) { this.Id = Id; } public String getName() { return this.Name; } public void setName(String Name) { this.Name = Name; } public int getAge() { return this.Age; } public void setAge(int Age) { this.Age = Age; } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 128553479) public void delete() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.delete(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 1942392019) public void refresh() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.refresh(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 713229351) public void update() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.update(this); } /** called by internal mechanisms, do not call yourself. */ @Generated(hash = 1862744651) public void __setDaoSession(DaoSession daoSession) { this.daoSession = daoSession; myDao = daoSession != null ? daoSession.getToOneDao() : null; } } package com.example.administrator.myapplication; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.NotNull; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Unique; import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.DaoException; /** * Created by Administrator on 2017/5/25. */ @Entity( nameInDb = "USER" ,active = true ,indexes = {@Index(value = "Id ASC")} ) public class User { @org.greenrobot.greendao.annotation.Id(autoincrement = true) @Property(nameInDb = "ID") @NotNull @Unique public Long Id; @Property(nameInDb = "NAME") @NotNull public String Name; @Property(nameInDb = "AGE") @NotNull public int Age; /** Used to resolve relations */ @Generated(hash = 2040040024) private transient DaoSession daoSession; /** Used for active entity operations. */ @Generated(hash = 1507654846) private transient UserDao myDao; @Generated(hash = 1925281583) public User(@NotNull Long Id, @NotNull String Name, int Age) { this.Id = Id; this.Name = Name; this.Age = Age; } @Generated(hash = 586692638) public User() { } public Long getId() { return this.Id; } public void setId(Long Id) { this.Id = Id; } public String getName() { return this.Name; } public void setName(String Name) { this.Name = Name; } public int getAge() { return this.Age; } public void setAge(int Age) { this.Age = Age; } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 128553479) public void delete() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.delete(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 1942392019) public void refresh() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.refresh(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. * Entity must attached to an entity context. */ @Generated(hash = 713229351) public void update() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } myDao.update(this); } /** called by internal mechanisms, do not call yourself. */ @Generated(hash = 2059241980) public void __setDaoSession(DaoSession daoSession) { this.daoSession = daoSession; myDao = daoSession != null ? daoSession.getUserDao() : null; } } 3,目前删除数据delete系列的方法只支持有主键的数据表,没主键的数据表目前只能通过sql语句来删除. 4,Insert系列方法,如果设置了主键并且主键为null则GreenDao会自动为我们添加设置主键(自增方式); 5,GreenDao缓存实现是将查询返回的结果保存到数组中如果有就从缓存的数组中查找,没有就从查询结果返回的游标中查找. 以Query.List()方法为例 public List<T> list() { //检查线程是否是同一线程,如果不是就抛出异常 checkThread(); //查询返回游标,一般情况下这个返回的游标是CrossProcessCursor对象 Cursor cursor = dao.getDatabase().rawQuery(sql, parameters); return daoAccess.loadAllAndCloseCursor(cursor); } 接着跳到 public List<T> loadAllAndCloseCursor(Cursor cursor) { return dao.loadAllAndCloseCursor(cursor); } 最终到 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; //如果是CrossProcessCursor对象 if (cursor instanceof CrossProcessCursor) { window = ((CrossProcessCursor) cursor).getWindow(); if (window != null) { // E.g. Robolectric has no Window at this point //如果两者数量相等,就将useFastCursor置为true 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 { //useFastCursor为false,IdentityScopeType置为IdentityScopeType.Session表示使用缓存 //这个是否useFastCursor就为true 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; } final protected T loadCurrent(Cursor cursor, int offset, boolean lock) { //如果identityScopeLong不为null,表示优先使用缓存中的数据 if (identityScopeLong != null) { if (offset != 0) { // Occurs with deep loads (left outer joins) if (cursor.isNull(pkOrdinal + offset)) { return null; } } long key = cursor.getLong(pkOrdinal + offset); 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); 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; } } 6,如果想清除指定表缓存,调用 DaoConfig ().clearIdentityScope (); 或者 AbstractDao.detachAll() /** * 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(); } } ,如果想清除所有表的缓存调用 DaoSession.clear () 如果想在查询返回结果中多返回几条数据库中没有的数据条数可以调用,可以按照这样做 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); } } } ,如果想在查询结果中返回少几条指定数据,但是不删除数据表中的数据记录可以这样做 /** 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; } } 以上只针对设置启用数据库缓存的情况 7,AbstractDao.refresh(T entity)是将entity实体恢复成数据表里的状态(如果启用了缓存也把缓存中的记录重置); AbstractDao.update(T entity)将对改实体的改动更新到数据表(如果启用了缓存,也把缓存中的记录更新); AbstractDao.delete(T entity)将该实体对应的数据表记录删除掉(如果启用了缓存,也把缓存中的记录删除掉); AbstractDao.save系列方法,如果数据库中存在该条数据就更新,如果不存在就添加. 8,GreenDao的delete系列删除数据记录的方法目前只支持有主键的数据表; 9,GreenDao也支持Rx操作,例如 RxDao<Student,Long> rxDAO=new RxDao < Student, Long > ( mDaoSession.getStudentDao ()); rxDAO.insertInTx ( sStudents ).subscribe ( new Action1 < Iterable < Student > > ( ) { @Override public void call ( Iterable < Student > pStudents ) { } } );