文章大綱
一、greenDao簡介
二、greenDao實戰
三、項目源碼下載
四、參考文章

一、greenDao簡介
1. 什么是greenDao
GreenDAO是一個開源的Android ORM(“對象/關系映射”),通過ORM(稱為“對象/關系映射”),在我們數據庫開發過程中節省了開發時間。

2. GreenDao的官方文檔
(1)GreenDao:適用於您的SQLite數據庫的Android ORM
(2)GreenDao的github地址
(3)GreenDao的Google討論區
(4)GreenDao 加密SQLCipher for Android官方說明地址
(5) GreenDao使用文檔
3. GreenDao原理
DAO的core library中有以下幾個核心類,也是后面常用到的,先來大概了解下他們的結構。

-
DaoMaster:Dao中的管理者。它保存了sqlitedatebase對象以及操作DAO classes(注意:不是對象)。其提供了一些創建和刪除table的靜態方法,其內部類OpenHelper和DevOpenHelper實現了
SQLiteOpenHelper,並創建數據庫的框架。 -
DaoSession:會話層。操作具體的DAO對象(注意:是對象),比如各種getter方法。
-
Daos:實際生成的某某DAO類,通常對應具體的java類,比如NoteDao等。其有更多的權限和方法來操作數據庫元素。
-
Entities:持久的實體對象。通常代表了一個數據庫row的標准java properties。
4. GreenDao的優點
(1)一個精簡的庫
(2)性能最大化
(3)內存開銷最小化
(4)易於使用的 APIs
(5)對 Android 進行高度優化
二、greenDao實戰
1. 添加依賴
在項目的build.gradle添加

在app的build.gradle添加

具體代碼如下:
apply plugin: 'com.android.application' //添加greendao相關的plugin apply plugin: 'org.greenrobot.greendao' android { compileSdkVersion 27 defaultConfig { applicationId "top.daxianwill.greendaodemo" minSdkVersion 15 targetSdkVersion 27 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } greendao{ //指定數據庫schema版本號,遷移等操作會用到; schemaVersion 1 //dao的包名,包名默認是entity所在的包; daoPackage 'com.greendao.gen' //生成數據庫文件的目錄; targetGenDir 'src/main/java' } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.0' //添加greendao相關依賴 implementation 'org.greenrobot:greendao:3.2.2' }
2. 創建實體類
package top.daxianwill.greendaodemo; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Transient; import org.greenrobot.greendao.annotation.Generated; /** * 創建數據庫實體類 * * @Entity 表示這個實體類一會會在數據庫中生成對應的表, @Id 表示該字段是id,注意該字段的數據類型為包裝類型Long @Property 則表示該屬性將作為表的一個字段,其中nameInDb看名字就知道這個屬性在數據庫中對應的數據名稱。 @Transient 該注解表示這個屬性將不會作為數據表中的一個字段。 @NotNull 表示該字段不可以為空 @Unique 表示該字段唯一 */ @Entity public class User { @Id private Long id; @Property(nameInDb = "NAME") private String name; @Transient private int tempUsageCount; // not persisted @Generated(hash = 873297011) public User(Long id, String name) { this.id = id; this.name = name; } @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; } }
注解介紹
(1)@Entity
用來聲明類實體,表示它將映射為數據表
@Entity()括號內可加入更詳細的設置,如:
nameInDb =“TABLE_NAME” ——> 聲明該表的表名,默認取類名
createInDb = true ——> 是否創建表,默認為true
generateConstructors = true ——> 是否生成含所有參數的構造函數,默認為true
generateGettersSetters = true ——> 是否生成getter/setter,默認為true
(2)@Id
用來聲明某變量為表的主鍵,類型使用Long
@Id()括號可加入autoincrement = true表明自增長
(3)@Unique
用來聲明某變量的值需為唯一值
(4)@NotNull
用來聲明某變量的值不能為null
(5)@Property
@Property(nameInDb = “URL”) 用來聲明某變量在表中的實際字段名為URL
(6)@Transient
用來聲明某變量不被映射到數據表中
(7)@ToOne、@ToMany
用來聲明”對一”和“對多”關系,下面舉例說明:
學生與學校之間一對多的關系(一個學生對應一個學校,一個學校對應有多個學生)
@Entity class Student{ //...省略其他變量 private long fk_schoolId;//外鍵 @ToOne(joinProperty = "fk_schoolId") private School school; } @Entity class School{ //...省略其他變量 @ToMany(referencedJoinProperty = "fk_schoolId") private List<Student> students; }
學生與課程之間“多對多”的關系(一個學生對應有多門課程,一門課程對應有多個學生)
@Entity class Student{ //...省略其他變量 @ToMany @JoinEntity( entity = StudentWithCourse.class, sourceProperty = "sId", targetProperty = "cId" ) private List<Course> courses; } @Entity class Course{ //...省略其他變量 @ToMany @JoinEntity( entity = StudentWithCourse.class, sourceProperty = "cId", targetProperty = "sId" ) private List<Course> courses; } @Entity class StudentWithCourse{ @Id private Long id; private Long sId; private Long cId; }
3. Make Project
利用上面注解寫好表實體后,通過Build—>Make Project重新編譯項目, 將會在表實體中自動生成構造方法和getter/setter方法,另外在指定(或默認)的包中生成DaoMaster、DaoSession以及表實體對應的Dao(如MovieCollectDao)。
DaoMaster:用於創建數據庫以及獲取DaoSession
DaoSession:用於獲取各個表對應的Dao類
各個表對應的Dao:提供了對表進行增刪改查的方法

4. 進行數據增刪改查
進行操作前,我們先獲取操作的對象
UserDao mUserDao = MyApplication.getInstances().getDaoSession().getUserDao();
進行數據增加
public void insert(){ mUser = new User(id++,"any"+id); mUserDao.insert(mUser); notifyListView(); }
補充:
(1)上述代碼講的是插入單條數據,插入多條數據方式為:
List<MovieCollect> listUserCollect; mUserDao.insertInTx(listUserCollect);
(2)插入或替換數據
//插入的數據如果已經存在表中,則替換掉舊數據(根據主鍵來檢測是否已經存在)
MovieCollect userCollect;
mUserDao.insertOrReplace(userCollect);//單個數據 List<MovieCollect> listUserCollect; mUserDao.insertOrReplace(listUserCollect);//一組數據
進行數據刪除
public void delete(){ long l = mUserDao.count() - 1; mUserDao.deleteByKey(l); notifyListView(); }
補充:
(1)上面代碼講的是刪除一條數據,刪除所有數據方式為:
mUserDao.deleteAll();
(2)刪除多條數據
List<User> listUserCollect; mUserDao.deleteInTx(listUserCollect);
進行數據修改
public void update(){ mUser = new User((long)3,"any0803"); mUserDao.update(mUser); notifyListView(); }
補充:
(1)上面代碼介紹的是修改單條數據,修改多條數據方式如下:
List<User> listUserCollect; mUserDao.updateInTx(listUserCollect);
進行數據查詢
public void loadAll(){ mUserList = mUserDao.loadAll();//查詢所有數據 notifyListView(); }
補充:
(1)上面代碼介紹的是查詢所有數據,查詢數據數量方式如下:
int count = mUserDao.count();
(2)精確(where)條件查詢
//查詢電影名為“肖申克的救贖”的電影 MovieCollect movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Title.eq("肖申克的救贖")).unique(); //查詢電影年份為2017的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.eq(2017)).list();
(3)模糊查詢(like)
//查詢電影名含有“傳奇”的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Title.like("傳奇")).list(); //查詢電影名以“我的”開頭的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Title.like("我的%")).list();
(4)區間查詢
//大於 //查詢電影年份大於2012年的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.gt(2012)).list(); //大於等於 //查詢電影年份大於等於2012年的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.ge(2012)).list(); //小於 //查詢電影年份小於2012年的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.lt(2012)).list(); //小於等於 //查詢電影年份小於等於2012年的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.le(2012)).list(); //介於中間 //查詢電影年份在2012-2017之間的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.between(2012,2017)).list();
(5)升序降序
//查詢電影年份大於2012年的電影,並按年份升序排序 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.gt(2012)).orderAsc(MovieCollectDao.Properties.Year).list(); //查詢電影年份大於2012年的電影,並按年份降序排序 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().where(MovieCollectDao.Properties.Year.gt(2012)).orderDesc(MovieCollectDao.Properties.Year).list();
(6)and/or
//and //查詢電影年份大於2012年且電影名以“我的”開頭的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().and(MovieCollectDao.Properties.Year.gt(2012), MovieCollectDao.Properties.Title.like("我的%")).list(); //or //查詢電影年份小於2012年或者大於2015年的電影 List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().or(MovieCollectDao.Properties.Year.lt(2012), MovieCollectDao.Properties.Year.gt(2015)).list();
5. 緩存處理
由於GreenDao默認開啟了緩存,所以當你調用A查詢語句取得X實體,然后對X實體進行修改並更新到數據庫,接着再調用A查詢語句取得X實體,會發現X實體的內容依舊是修改前的。其實你的修改已經更新到數據庫中,只是查詢采用了緩存,所以直接返回了第一次查詢的實體。
解決方法:查詢前先清空緩存,清空方法如下
//清空所有數據表的緩存數據 DaoSession daoSession = MyApplication.getInstances().getDaoSession(); daoSession .clear(); //清空某個數據表的緩存數據 UserDao userDao = MyApplication.getInstances().getDaoSession().getUserDao(); userDao.detachAll();
6. 數據庫加密
添加依賴
compile 'net.zetetic:android-database-sqlcipher:3.5.7'//使用加密數據庫時需要添加
獲取操作的數據庫對象
mSQLiteOpenHelper = new MySQLiteOpenHelper(MyApplication.getInstance(), DB_NAME, null);//建庫 mDaoMaster = new DaoMaster(mSQLiteOpenHelper.getEncryptedWritableDb("你的密碼"));//加密 //mDaoMaster = new DaoMaster(mSQLiteOpenHelper.getWritableDatabase()); mDaoSession = mDaoMaster.newSession();
溫馨提示:
(1)使用上面步驟得到的DaoSession進行具體的數據表操作。
(2)如果運行后報無法加載有關so庫的異常,請對項目進行clean和rebuild。
7. 數據庫版本升級
在版本迭代時,我們經常需要對數據庫進行升級,而GreenDAO默認的DaoMaster.DevOpenHelper在進行數據升級時,會把舊表刪除,然后創建新表,並沒有遷移舊數據到新表中,從而造成數據丟失。
這在實際中是不可取的,因此我們需要作出調整。下面介紹數據庫升級的步驟與要點。
第一步
新建一個類,繼承DaoMaster.DevOpenHelper,重寫onUpgrade(Database db, int oldVersion, int newVersion)方法,在該方法中使用MigrationHelper進行數據庫升級以及數據遷移。
網上有不少MigrationHelper的源碼,這里采用的是https://github.com/yuweiguocn/GreenDaoUpgradeHelper中的MigrationHelper,它主要是通過創建一個臨時表,將舊表的數據遷移到新表中,大家可以去看下源碼。
public class MyOpenHelper extends DaoMaster.OpenHelper { public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { super(context, name, factory); } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { //把需要管理的數據庫表DAO作為最后一個參數傳入到方法中 MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() { @Override public void onCreateAllTables(Database db, boolean ifNotExists) { DaoMaster.createAllTables(db, ifNotExists); } @Override public void onDropAllTables(Database db, boolean ifExists) { DaoMaster.dropAllTables(db, ifExists); } }, MovieCollectDao.class); } }
然后使用MyOpenHelper替代DaoMaster.DevOpenHelper來進行創建數據庫等操作
mSQLiteOpenHelper = new MyOpenHelper(MyApplication.getInstance(), DB_NAME, null);//建庫 mDaoMaster = new DaoMaster(mSQLiteOpenHelper.getWritableDatabase()); mDaoSession = mDaoMaster.newSession();
第二步
在表實體中,調整其中的變量(表字段),一般就是新增/刪除/修改字段。注意:
(1)新增的字段或修改的字段,其變量類型應使用基礎數據類型的包裝類,如使用Integer而不是int,避免升級過程中報錯。
(2)根據MigrationHelper中的代碼,升級后,新增的字段和修改的字段,都會默認被賦予null值。
第三步
將原本自動生成的構造方法以及getter/setter方法刪除,重新Build—>Make Project進行生成。
第四步
修改Module下build.gradle中數據庫的版本號schemaVersion ,遞增加1即可,最后運行app
greendao{
//數據庫版本號,升級時進行修改 schemaVersion 2 //dao的包名,包名默認是entity所在的包; daoPackage 'com.greendao.gen' //生成數據庫文件的目錄; targetGenDir 'src/main/java' }
8. 代碼混淆
  在proguard-rules.pro文件中添加以下內容進行混淆配置
# greenDAO開始 -keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { public static java.lang.String TABLENAME; } -keep class **$Properties # If you do not use SQLCipher: -dontwarn org.greenrobot.greendao.database.** # If you do not use RxJava: -dontwarn rx.** # greenDAO結束 # 如果按照上面介紹的加入了數據庫加密功能,則需添加一下配置 #sqlcipher數據庫加密開始 -keep class net.sqlcipher.** {*;} -keep class net.sqlcipher.database.** {*;} #sqlcipher數據庫加密結束
三、項目源碼下載
鏈接:https://pan.baidu.com/s/1uSIvGWPGwIjQp0YTd962AA
密碼:iel2