1. 程式人生 > 其它 >greendao 無法自動生成檔案_GreenDao的全面使用指南(原始碼+案列)

greendao 無法自動生成檔案_GreenDao的全面使用指南(原始碼+案列)

技術標籤:greendao 無法自動生成檔案

好久未更新,趕快把乾貨整理出來,這篇文章是去年10月份寫的,算是自己寫的技術文章中讀者反響挺不錯的一篇,可能有學習GreenDao的朋友看過,這裡再推薦一波,希望能幫助大家更深入的學習瞭解GreenDao的使用。原文如下:

之前在開發過程中,資料庫基本上會使用Litepal和SQlite自己寫,最近換新環境,公司原先使用的資料庫就是GreenDao,在各種情況的作用下,準備瞭解下GreenDao,順便寫一篇文章記錄下GreenDao的基本使用!大家在使用GreenDao的時候遇到什麼問題,歡迎給我留言。

迴歸正題,不再扯沒用的了!本文主要從如下幾個方面進行講解:1. 儲存的資料庫結構 2. GreenDao的優缺點 3. GreenDao的使用配置 4. 使用GreenDao實現資料的增刪改查 5. GreenDao的註解使用 6. GreenDao的關係處理 7. GreenDao的升級 8. GreenDao資料庫加密 9. 專案地址 10 總結 11. 參考部落格

咱們先看一波最終的效果圖:文章最後有專案地址;

GreenDao的使用

1. 儲存的資料庫結構

學習資料庫之前,我們先得設計自己的資料庫,不多廢話,下面是我此次學習的資料庫結構,後面所有的資料請參考這個圖進行學習:

GreenDao的使用

2. GreenDao的介紹

簡單的GreenDao的介紹,閒麻煩的可以直接跳到GreenDao使用開始看。

什麼是GreenDao?

GreenDAO是一個開源的Android ORM(“物件/關係對映”),通過ORM(稱為“物件/關係對映”),在我們資料庫開發過程中節省了開發時間!

GreenDao的使用

GreenDao的官方文件

  1. GreenDao:適用於您的SQLite資料庫的Android ORM

  2. GreenDao的github地址

  3. GreenDao的線上討論區

  4. GreenDao 加密SQLCipher for Android官方說明地址

  5. GreenDao使用文件

GreenDao的作用?

通過GreenDao,我們可以更快速的操作資料庫,我們可以使用簡單的面相物件的API來儲存,更新,刪除和查詢Java物件。

GreenDao的優缺點?

  1. 高效能,下面是官方給出的關於GreenDao,OrmLite和ActiveAndroid三種ORM解決方案的資料統計圖:

    GreenDao的使用
  2. 易於使用的強大API,涵蓋關係和連線;

  3. 最小的記憶體消耗;

  4. 小庫大小(<100KB)以保持較低的構建時間並避免65k方法限制;

  5. 資料庫加密:greenDAO支援SQLCipher,以確保使用者的資料安全;

3. GreenDao的使用

GreenDao的核心類有三個:分別是DaoMaster,DaoSession,XXXDao,這三個類都會自動建立,無需自己編寫建立!

  • DaoMaster::DaoMaster儲存資料庫物件(SQLiteDatabase)並管理特定模式的DAO類(而不是物件)。它有靜態方法來建立表或刪除它們。它的內部類OpenHelper和DevOpenHelper是SQLiteOpenHelper實現,它們在SQLite資料庫中建立模式。

  • DaoSession:管理特定模式的所有可用DAO物件,您可以使用其中一個getter方法獲取該物件。DaoSession還提供了一些通用的永續性方法,如實體的插入,載入,更新,重新整理和刪除。

  • XXXDao:資料訪問物件(DAO)持久存在並查詢實體。對於每個實體,greenDAO生成DAO。它具有比DaoSession更多的永續性方法,例如:count,loadAll和insertInTx。

  • Entities :可持久化物件。通常, 實體物件代表一個數據庫行使用標準 Java 屬性(如一個POJO 或 JavaBean )。

    GreenDao的使用

1. 匯入Gradle外掛和Dao程式碼生成

要在Android專案中使用GreenDao,您需要新增GreenDao Gradle外掛並新增GreenDao庫:

  1. 匯入外掛

 1//在Project的build.gradle檔案中新增:
2buildscript{
3repositories{
4jcenter()
5mavenCentral()//addrepository
6}
7dependencies{
8classpath'com.android.tools.build:gradle:3.1.2'
9classpath'org.greenrobot:greendao-gradle-plugin:3.2.2'//addplugin
10}
11}
  1. 配置相關依賴

1//在Moudle:app的build.gradle檔案中新增:
2applyplugin:'com.android.application'
3applyplugin:'org.greenrobot.greendao'//applyplugin
4
5dependencies{
6implementation'org.greenrobot:greendao:3.2.2'//addlibrary
7}
  1. 配置資料庫相關資訊

1greendao{
2schemaVersion1//資料庫版本號
3daoPackage'com.aserbao.aserbaosandroid.functions.database.greenDao.db'
4//設定DaoMaster、DaoSession、Dao包名
5targetGenDir'src/main/java'//設定DaoMaster、DaoSession、Dao目錄
6generateTestsfalse//設定為true以自動生成單元測試。
7targetGenDirTests'src/main/java'//應儲存生成的單元測試的基本目錄。預設為 src / androidTest / java。
8}

配置完成,在Android Studio中使用Build> Make Project,重寫build專案,GreenDao整合完成!

2. 建立儲存物件實體類

使用GreenDao儲存資料只需要在儲存資料類前面宣告@Entity註解就讓GreenDao為其生成必要的程式碼:

 [email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5@Unique
6intstudentNo;//學號
7intage;//年齡
8StringtelPhone;//手機號
9Stringsex;//性別
10Stringname;//姓名
11Stringaddress;//家庭住址
12StringschoolName;//學校名字
13Stringgrade;//幾年級
14……getterandsetterandconstructormethod……
15}

3. GreenDao初始化

我們可以在Application中維持一個全域性的會話。我們在Applicaiton進行資料庫的初始化操作:

 1/** 2*初始化GreenDao,直接在Application中進行初始化操作 3*/
4privatevoidinitGreenDao(){
5DaoMaster.DevOpenHelperhelper=newDaoMaster.DevOpenHelper(this,"aserbao.db");
6SQLiteDatabasedb=helper.getWritableDatabase();
7DaoMasterdaoMaster=newDaoMaster(db);
8daoSession=daoMaster.newSession();
9}
10
11privateDaoSessiondaoSession;
12publicDaoSessiongetDaoSession(){
13returndaoSession;
14}

初始化完成之後重新rebuild一下專案會發現在設定的targetGenDir的目錄生成三個類檔案,這個是GreenDao自動生成的!說明資料庫已經連線好了,咱們接下來只需要進行資料庫的增刪改查操作就行了。Let's Go!

4. 使用GreenDao實現增刪改查

1. 增

insert() 插入資料

 1@Override
2publicvoidinsertData(Things){
3DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
4for(inti=0;i1000;i++){
5Studentstudent=newStudent();
6student.setStudentNo(i);
7intage=mRandom.nextInt(10)+10;
8student.setAge(age);
9student.setTelPhone(RandomValue.getTel());
10StringchineseName=RandomValue.getChineseName();
11student.setName(chineseName);
12if(i%2==0){
13student.setSex("男");
14}else{
15student.setSex("女");
16}
17student.setAddress(RandomValue.getRoad());
18student.setGrade(String.valueOf(age%10)+"年紀");
19student.setSchoolName(RandomValue.getSchoolName());
20daoSession.insert(student);
21}
22}

insertOrReplace()資料存在則替換,資料不存在則插入

 [email protected]
2publicvoidinsertData(Things){
3DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
4for(inti=0;i1000;i++){
5Studentstudent=newStudent();
6student.setStudentNo(i);
7intage=mRandom.nextInt(10)+10;
8student.setAge(age);
9student.setTelPhone(RandomValue.getTel());
10StringchineseName=RandomValue.getChineseName();
11student.setName(chineseName);
12if(i%2==0){
13student.setSex("男");
14}else{
15student.setSex("女");
16}
17student.setAddress(RandomValue.getRoad());
18student.setGrade(String.valueOf(age%10)+"年紀");
19student.setSchoolName(RandomValue.getSchoolName());
20daoSession.insertOrReplace(student);//插入或替換
21}
22}

2. 刪

刪除有兩種方式:delete()和deleteAll();分別表示刪除單個和刪除所有。

1@Override2publicvoiddeleteData(Students){
3DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
4daoSession.delete(s);
5}
1@Override
2publicvoiddeleteAll(){
3DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
4daoSession.deleteAll(Student.class);
5}

3. 改

通過update來進行修改:

[email protected]
2publicvoidupdataData(Students){
3DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
4daoSession.update(s);
5}

4. 查

查詢的方法有:

  • loadAll():查詢所有資料。

  • queryRaw():根據條件查詢。

  • queryBuilder() : 方便查詢的建立,後面詳細講解。

1publicListqueryAll(){
2Liststudents=daoSession.loadAll(Student.class);3returnstudents;4}
1@Override
2publicvoidqueryData(Strings){
3Liststudents=daoSession.queryRaw(Student.class,"whereid=?",s);4mDataBaseAdapter.addNewStudentData(students);5}

4. QueryBuilder的使用

編寫SQL可能很困難並且容易出現錯誤,這些錯誤僅在執行時才會被注意到。該QueryBuilder的類可以讓你建立你的實體,而不SQL自定義查詢,並有助於在編譯時已檢測錯誤。

我們先講下QueryBuilder的常見方法:

  • where(WhereCondition cond, WhereCondition… condMore): 查詢條件,引數為查詢的條件!

  • or(WhereCondition cond1, WhereCondition cond2, WhereCondition… condMore): 巢狀條件或者,用法同or。

  • and(WhereCondition cond1, WhereCondition cond2, WhereCondition… condMore): 巢狀條件且,用法同and。

  • join(Property sourceProperty, Class

    destinationEntityClass):多表查詢,後面會講。
    輸出結果有四種方式,選擇其中一種最適合的即可,list()返回值是List,而其他三種返回值均實現Closeable,需要注意的不使用資料時遊標的關閉操作:
  • list ()所有實體都載入到記憶體中。結果通常是一個沒有魔法的 ArrayList。最容易使用。

  • listLazy ()實體按需載入到記憶體中。首次訪問列表中的元素後,將載入並快取該元素以供將來使用。必須關閉。

  • listLazyUncached ()實體的“虛擬”列表:對列表元素的任何訪問都會導致從資料庫載入其資料。必須關閉。

  • listIterator ()讓我們通過按需載入資料(懶惰)來迭代結果。資料未快取。必須關閉。

GreenDao中SQL語句的縮寫,我們也瞭解下,原始碼在Property中,使用的時候可以自己點進去查詢即可:

  • eq():"equal ('=?')" 等於;

  • notEq() :"not equal ('<>?')" 不等於;

  • like():" LIKE ?" 值等於;

  • between():" BETWEEN ? AND ?" 取中間範圍;

  • in():" IN (" in命令;

  • notIn():" NOT IN (" not in 命令;

  • gt():">?" 大於;

  • lt():" " 小於;

  • ge():">=?" 大於等於;

  • le():"<=? " 小於等於;

  • isNull():" IS NULL" 為空;

  • isNotNull():" IS NOT NULL" 不為空;

  • orderAsc() 按某個屬性升序排;

  • orderDesc() 按某個屬性降序排;

1. 使用QueryBuilder進行查詢操作

1. 簡單條件查詢

查詢當前Student表的所有的資料:

1publicListqueryAllList(){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3QueryBuilderqb=daoSession.queryBuilder(Student.class);4Listlist=qb.list();//查出所有的資料5returnlist;6}

查詢Name為“一”的所有Student:

1publicListqueryListByMessage(Stringname){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3QueryBuilderqb=daoSession.queryBuilder(Student.class);4QueryBuilderstudentQueryBuilder=qb.where(StudentDao.Properties.Name.eq("一")).orderAsc(StudentDao.Properties.Name);5ListstudentList=studentQueryBuilder.list();//查出當前對應的資料6returnlist;7}

2. 原始查詢

通過原始的SQL查詢語句進行查詢!其實上面有提到QueryBuilder的目的就是方便快捷的編寫SQL查詢語句,避免我們自己在編寫過程中出錯!簡單介紹下通過QueryBuilder編寫資料庫,方式方法如下 :

 1publicListqueryListBySqL(){
2//查詢ID大於5的所有學生
3DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
4Queryquery=daoSession.queryBuilder(Student.class).where( 5newWhereCondition.StringCondition("_IDIN"+ 6"(SELECT_IDFROMSTUDENTWHERE_ID>5)") 7).build(); 8Listlist=query.list(); 9returnlist;10}

3. 巢狀條件查詢

查詢Id大於5小於10,且Name值為"一"的資料:

1publicListqueryList(){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3QueryBuilderqb=daoSession.queryBuilder(Student.class);4qb=daoSession.queryBuilder(Student.class);5Listlist2=qb.where(StudentDao.Properties.Name.eq("一"),6qb.and(StudentDao.Properties.Id.gt(5),7StudentDao.Properties.Id.le(50))).list();8returnlist2;9}

取10條Id大於1的資料,且偏移2條

1publicListqueryListByOther(){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3QueryBuilderqb=daoSession.queryBuilder(Student.class);45//搜尋條件為Id值大於1,即結果為[2,3,4,5,6,7,8,9,10,11];6//offset(2)表示往後偏移2個,結果為[4,5,6,7,8,9,10,11,12,13];7Listlist=qb.where(StudentDao.Properties.Id.gt(1)).limit(10).offset(2).list();8returnlist;9}

4. 多次執行查詢

使用QueryBuilder構建查詢後,可以重用 Query物件以便稍後執行查詢。這比始終建立新的Query物件更有效。如果查詢引數沒有更改,您可以再次呼叫list / unique方法。可以通過setParameter方法來修改條件引數值,特別注意:如果查詢條件不同,則需要構建新的QueryBuilder:

 1publicListqueryListByMoreTime(){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3QueryBuilderqb=daoSession.queryBuilder(Student.class); 4 5//搜尋條件為Id值大於1,即結果為[2,3,4,5,6,7,8,9,10,11]; 6//offset(2)表示往後偏移2個,結果為[4,5,6,7,8,9,10,11,12,13]; 7Queryquery=qb.where(StudentDao.Properties.Id.gt(1)).limit(10).offset(2).build(); 8Listlist=query.list(); 910//通過SetParameter來修改上面的查詢條件,比如我們將上面條件修改取10條Id值大於5,往後偏移兩位的資料,方法如下!11query.setParameter(0,5);12Listlist1=query.list();13returnlist1;14}

5. 在多個執行緒中使用QueryBuilder

如果在多個執行緒中使用查詢,則必須呼叫 forCurrentThread ()以獲取當前執行緒的Query例項。Query的物件例項繫結到構建查詢的擁有執行緒。

這使您可以安全地在Query物件上設定引數,而其他執行緒不會干擾。如果其他執行緒嘗試在查詢上設定引數或執行繫結到另一個執行緒的查詢,則會丟擲異常。像這樣,您不需要同步語句。實際上,您應該避免鎖定,因為如果併發事務使用相同的Query物件,這可能會導致死鎖。

每次呼叫forCurrentThread ()時, 引數都會在使用其構建器構建查詢時設定為初始引數。

2. 使用QueryBuilder進行批量刪除操作

使用QueryBuilder進行批量刪除操作,不會刪除單個實體,但會刪除符合某些條件的所有實體。要執行批量刪除,請建立QueryBuilder,呼叫其 buildDelete ()方法,然後執行返回的 DeleteQuery。

例子:刪除資料庫中id大於5的所有其他資料

1publicbooleandeleteItem(){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3QueryBuilderwhere=daoSession.queryBuilder(Student.class).where(StudentDao.Properties.Id.gt(5));4DeleteQuerydeleteQuery=where.buildDelete();5deleteQuery.executeDeleteWithoutDetachingEntities();6returnfalse;7}

5. 註解講解

從GreenDao 3 使用註解來定義模型和實體,前面也講過,通過註解的使用可以快速構建資料庫表,包括設定主鍵,自增,值是否唯一等等等……

下面我們來看下註解的簡單使用:

 [email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5@Unique
6intstudentNo;//學號
7intage;//年齡
8StringtelPhone;//手機號
9Stringsex;//性別
10Stringname;//姓名
11Stringaddress;//家庭住址
12StringschoolName;//學校名字
13Stringgrade;//幾年級
14……getterandsetterandconstructormethod……
15}

1. @Entity註解

@Entity是GreenDao必不可少的註解,只有在實體類中使用了@Entity註解GreenDao才會建立對應的表。當然我們也可以使用@Entity配置一些細節:

  • schema:如果你有多個架構,你可以告訴GreenDao當前屬於哪個架構。

  • active:標記一個實體處於活躍狀態,活動實體有更新、刪除和重新整理方法。

  • nameInDb:在資料中使用的別名,預設使用的是實體的類名。

  • indexes:標記如果DAO應該建立資料庫表(預設為true),如果您有多個實體對映到一個表,或者表的建立是在greenDAO之外進行的,那麼將其設定為false。

  • createInDb:標記建立資料庫表。

  • generateGettersSetters:如果缺少,是否應生成屬性的getter和setter方法。

 [email protected](
2
3schema="myschema",
4active=true,
5nameInDb="AWESOME_USERS",
6indexes={
7@Index(value="messageDESC",unique=true)
8},
9createInDb=false,
10generateConstructors=true,
11generateGettersSetters=true
12)
13publicclassStudent{
14……
15}

2. 基礎屬性註解(@Id,@Property,@NotNull,@Transient)

@Id
@Id註解選擇 long / Long屬性作為實體ID。在資料庫方面,它是主鍵。引數autoincrement = true 表示自增,id不給賦值或者為賦值為null即可(這裡需要注意,如果要實現自增,id必須是Long,為long不行!)。

[email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5……
6}

@Property
允許您定義屬性對映到的非預設列名。如果不存在,GreenDAO將以SQL-ish方式使用欄位名稱(大寫,下劃線而不是camel情況,例如 name將成為 NAME)。注意:您當前只能使用內聯常量來指定列名。

[email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5@Property(nameInDb="name")//設定了,資料庫中的表格屬性名為"name",如果不設定,資料庫中表格屬性名為"NAME"
6Stringname;
7……
8}

@NotNull :設定資料庫表當前列不能為空 。

@Transient :新增次標記之後不會生成資料庫表的列。標記要從永續性中排除的屬性。將它們用於臨時狀態等。或者,您也可以使用Java中的transient關鍵字。

3. 索引註解

  • @Index:使用@Index作為一個屬性來建立一個索引,通過name設定索引別名,也可以通過unique給索引新增約束。

  • @Unique:向索引新增UNIQUE約束,強制所有值都是唯一的。

[email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5@Property(nameInDb="name")
6@Index(unique=true)
7Stringname;
8……
9}

注意: 上面這種情況,約定name為唯一值,向資料庫中通過insert方法繼續新增已存在的name資料,會拋異常:

110-0820:59:46.27431939-31939/com.example.aserbao.aserbaosandroidE/AndroidRuntime:FATALEXCEPTION:main
2Process:com.example.aserbao.aserbaosandroid,PID:31939
3android.database.sqlite.SQLiteConstraintException:UNIQUEconstraintfailed:STUDENT.name(Sqlitecode2067),(OSerror-2:Nosuchfileordirectory)
4……

若使用insertOrReplace()方法新增資料,當前資料庫中不會有重複的資料,但是重複的這條資料的id會被修改!若專案中有用到id欄位進行排序的話,這一點需要特別注意。

4. 關係註解

關係型註解GreenDao中主要就兩個:

  • @ToOne:定義與另一個實體(一個實體物件)的關係

  • @ToMany:定義與多個實體物件的關係
    至於如何使用,我們馬上就講。

6. 一對一,一對多,多對多關係表的建立

平常專案中,我們經常會使用到多表關聯,如文章開頭所說的資料庫表結構設定的那樣!接下來我們來講如何通過GreenDao實現多表關聯。

1. 一對一

一個學生對應一個身份證號:
做法:

  1. 我們在Student中設定一個註解@ToOne(joinProperty = "name")

  2. 在建立Student的時候,將對應的資料傳遞給IdCard;
    程式碼部分:

學生Student程式碼:

 [email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5@Unique
6intstudentNo;//學號
7intage;//年齡
8StringtelPhone;//手機號
9Stringsex;//性別
10Stringname;//姓名
11Stringaddress;//家庭住址
12StringschoolName;//學校名字
13Stringgrade;//幾年級
14@ToOne(joinProperty="name")
15IdCardstudent;
16……getterandsetter……
17}

身份證IdCard程式碼:

[email protected]
2publicclassIdCard{
3@Id
4StringuserName;//使用者名稱
5@Unique
6StringidNo;//身份證號
7……getterandsetter……
8}

insert一組資料:

 1publicvoidaddStudent(){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3Studentstudent=newStudent();
4student.setStudentNo(i);
5intage=mRandom.nextInt(10)+10;
6student.setAge(age);
7student.setTelPhone(RandomValue.getTel());
8StringchineseName=RandomValue.getChineseName();
9student.setName(chineseName);
10if(i%2==0){
11student.setSex("男");
12}else{
13student.setSex("女");
14}
15student.setAddress(RandomValue.getRoad());
16student.setGrade(String.valueOf(age%10)+"年紀");
17student.setSchoolName(RandomValue.getSchoolName());
18daoSession.insert(student);
19
20//插入對應的IdCard資料
21IdCardidCard=newIdCard();
22idCard.setUserName(userName);
23idCard.setIdNo(RandomValue.getRandomID());
24daoSession.insert(idCard);
25}

ok,資料可以了!現在資料庫表插入完成了。

2. 一對多

一個人擁有多個信用卡
做法:

  1. 在我們在Student中設定@ToMany(referencedJoinProperty = "id");

  2. 我們在CreditCard中設定編寫對應的id主鍵;

Student的程式碼:

 [email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5
6@Unique
7intstudentNo;//學號
8
9intage;//年齡
10StringtelPhone;//手機號
11Stringsex;//性別
12Stringname;//姓名
13Stringaddress;//家庭住址
14StringschoolName;//學校名字
15Stringgrade;//幾年級
16
17@ToMany(referencedJoinProperty="id")//這個id是對應在CreditCard中的id
18ListcreditCardsList;19……getterandsetter……20}

CreditCard的程式碼:

 [email protected]
2publicclassCreditCard{
3@Id
4Longid;
5LonguserId;
6StringuserName;//持有者名字
7StringcardNum;//卡號
8StringwhichBank;//哪個銀行的
9intcardType;//卡等級,分類0~5
10……getterandsetter……
11}

新增資料程式碼:

 1publicvoidaddStudent(){
2DaoSessiondaoSession=((AserbaoApplication)getApplication()).getDaoSession();
3Studentstudent=newStudent();
4student.setStudentNo(i);
5intage=mRandom.nextInt(10)+10;
6student.setAge(age);
7student.setTelPhone(RandomValue.getTel());
8StringchineseName=RandomValue.getChineseName();
9student.setName(chineseName);
10if(i%2==0){
11student.setSex("男");
12}else{
13student.setSex("女");
14}
15student.setAddress(RandomValue.getRoad());
16student.setGrade(String.valueOf(age%10)+"年紀");
17student.setSchoolName(RandomValue.getSchoolName());
18daoSession.insert(student);
19
20//插入對應的CreditCard資料
21for(intj=0;j5)+1;j++){
22CreditCardcreditCard=newCreditCard();
23creditCard.setUserId(id);
24creditCard.setUserName(userName);
25creditCard.setCardNum(String.valueOf(random.nextInt(899999999)+100000000)+String.valueOf(random.nextInt(899999999)+100000000));
26creditCard.setWhichBank(RandomValue.getBankName());
27creditCard.setCardType(random.nextInt(10));
28daoSession.insert(creditCard);
29}
30}

3. 多對多

一個學生有多個老師,老師有多個學生。
做法:

  1. 我們需要建立一個學生老師管理器(StudentAndTeacherBean),用來對應學生和老師的ID;

  2. 我們需要在學生物件中,添加註解:

    @ToMany
    @JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = "studentId",targetProperty = "teacherId")
    ListteacherList;

  3. 我們需要在老師物件中,添加註解:@ToMany

    @JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = "teacherId",targetProperty = "studentId")
    ListstudentList;

StudentAndTeacherBean程式碼:

[email protected]
2publicclassStudentAndTeacherBean{
3@Id(autoincrement=true)
4Longid;
5LongstudentId;//學生ID
6LongteacherId;//老師ID
7……getterandsetter……
8}

Student 程式碼:

 [email protected]
2publicclassStudent{
3@Id(autoincrement=true)
4Longid;
5@Unique
6intstudentNo;//學號
7intage;//年齡
8StringtelPhone;//手機號
9Stringsex;//性別
10Stringname;//姓名
11Stringaddress;//家庭住址
12StringschoolName;//學校名字
13Stringgrade;//幾年級
14@ToMany
15@JoinEntity(entity=StudentAndTeacherBean.class,sourceProperty="studentId",targetProperty="teacherId")
16ListteacherList;17……getterandsetter……18}

Teacher程式碼:

 [email protected]
2publicclassTeacher{
3@Id(autoincrement=true)
4Longid;
5@Unique
6intteacherNo;//職工號
7intage;//年齡
8Stringsex;//性別
9StringtelPhone;
10Stringname;//姓名
11StringschoolName;//學校名字
12Stringsubject;//科目
13
14@ToMany
15@JoinEntity(entity=StudentAndTeacherBean.class,sourceProperty="teacherId",targetProperty="studentId")
16ListstudentList;17……getterandsetter……18}

資料新增:

 1publicvoidaddData(){
2Studentstudent=newStudent();
3student.setStudentNo(i);
4intage=mRandom.nextInt(10)+10;
5student.setAge(age);
6student.setTelPhone(RandomValue.getTel());
7StringchineseName=RandomValue.getChineseName();
8student.setName(chineseName);
9if(i%2==0){
10student.setSex("男");
11}else{
12student.setSex("女");
13}
14student.setAddress(RandomValue.getRoad());
15student.setGrade(String.valueOf(age%10)+"年紀");
16student.setSchoolName(RandomValue.getSchoolName());
17daoSession.insert(student);
18
19Collections.shuffle(teacherList);
20for(intj=0;j8)+1;j++){
21if(j22Teacherteacher=teacherList.get(j);
23StudentAndTeacherBeanteacherBean=newStudentAndTeacherBean(student.getId(),teacher.getId());
24daoSession.insert(teacherBean);
25}
26}
27}

好了,成功;

7. 資料庫的升級

GreenDao的OpenHelper下有個 onUpgrade(Database db, int oldVersion, int newVersion)方法,當設定的資料庫版本改變時,在資料庫初始化的時候就會回撥到這個方法,我們可以通過繼承OpenHelper重寫onUpgrade方法來實現資料庫更新操作:

GreenDao的升級思路:

  1. 建立臨時表TMP_,複製原來的資料庫到臨時表中;

  2. 刪除之前的原表;

  3. 建立新表;

  4. 將臨時表中的資料複製到新表中,最後將TMP_表刪除掉;

ok,思路就是這樣, 總共兩個類:一個MyDaoMaster(OpenHelper繼承類),一個MigrationHelper(資料庫操作類) 下面是程式碼編寫:

修改Application中的DaoMaster的建立:

1MyDaoMasterhelper=newMyDaoMaster(this,"aserbaos.db");
2//DaoMaster.DevOpenHelperhelper=newDaoMaster.DevOpenHelper(this,"aserbao.db");
3SQLiteDatabasedb=helper.getWritableDatabase();
4DaoMasterdaoMaster=newDaoMaster(db);
5daoSession=daoMaster.newSession();

MyDaoMaster程式碼:

 1publicclassMyDaoMasterextendsOpenHelper{
2privatestaticfinalStringTAG="MyDaoMaster";
3publicMyDaoMaster(Contextcontext,Stringname){
4super(context,name);
5}
6
7publicMyDaoMaster(Contextcontext,Stringname,SQLiteDatabase.CursorFactoryfactory){
8super(context,name,factory);
9}
10
11@Override
12publicvoidonUpgrade(Databasedb,intoldVersion,intnewVersion){
13super.onUpgrade(db,oldVersion,newVersion);
14MigrationHelper.migrate(db,newMigrationHelper.ReCreateAllTableListener(){
15@Override
16publicvoidonCreateAllTables(Databasedb,booleanifNotExists){
17DaoMaster.createAllTables(db,ifNotExists);
18}
19@Override
20publicvoidonDropAllTables(Databasedb,booleanifExists){
21DaoMaster.dropAllTables(db,ifExists);
22}
23},ThingDao.class);
24Log.e(TAG,"onUpgrade:"+oldVersion+"newVersion="+newVersion);
25}
26}

MigrationHelper 程式碼:

  1publicfinalclassMigrationHelper{
2
3publicstaticbooleanDEBUG=false;
4privatestaticStringTAG="MigrationHelper";
5privatestaticfinalStringSQLITE_MASTER="sqlite_master";
6privatestaticfinalStringSQLITE_TEMP_MASTER="sqlite_temp_master";
7
8privatestaticWeakReferenceweakListener; 9 10publicinterfaceReCreateAllTableListener{ 11voidonCreateAllTables(Databasedb,booleanifNotExists); 12voidonDropAllTables(Databasedb,booleanifExists); 13} 14 15publicstaticvoidmigrate(SQLiteDatabasedb,ClassextendsAbstractDao,?>>...daoClasses){ 16printLog("【TheOldDatabaseVersion】"+db.getVersion()); 17Databasedatabase=newStandardDatabase(db); 18migrate(database,daoClasses); 19} 20 21publicstaticvoidmigrate(SQLiteDatabasedb,ReCreateAllTableListenerlistener,ClassextendsAbstractDao,?>>...daoClasses){ 22weakListener=newWeakReference<>(listener); 23migrate(db,daoClasses); 24} 25 26publicstaticvoidmigrate(Databasedatabase,ReCreateAllTableListenerlistener,ClassextendsAbstractDao,?>>...daoClasses){ 27weakListener=newWeakReference<>(listener); 28migrate(database,daoClasses); 29} 30 31publicstaticvoidmigrate(Databasedatabase,ClassextendsAbstractDao,?>>...daoClasses){ 32printLog("【Generatetemptable】start"); 33generateTempTables(database,daoClasses); 34printLog("【Generatetemptable】complete"); 35 36ReCreateAllTableListenerlistener=null; 37if(weakListener!=null){ 38listener=weakListener.get(); 39} 40 41if(listener!=null){ 42listener.onDropAllTables(database,true); 43printLog("【Dropalltablebylistener】"); 44listener.onCreateAllTables(database,false); 45printLog("【Createalltablebylistener】"); 46}else{ 47dropAllTables(database,true,daoClasses); 48createAllTables(database,false,daoClasses); 49} 50printLog("【Restoredata】start"); 51restoreData(database,daoClasses); 52printLog("【Restoredata】complete"); 53} 54 55privatestaticvoidgenerateTempTables(Databasedb,ClassextendsAbstractDao,?>>...daoClasses){ 56for(inti=0;i 57StringtempTableName=null; 58 59DaoConfigdaoConfig=newDaoConfig(db,daoClasses[i]); 60StringtableName=daoConfig.tablename; 61if(!isTableExists(db,false,tableName)){ 62printLog("【NewTable】"+tableName); 63continue; 64} 65try{ 66tempTableName=daoConfig.tablename.concat("_TEMP"); 67StringBuilderdropTableStringBuilder=newStringBuilder(); 68dropTableStringBuilder.append("DROPTABLEIFEXISTS").append(tempTableName).append(";"); 69db.execSQL(dropTableStringBuilder.toString()); 70 71StringBuilderinsertTableStringBuilder=newStringBuilder(); 72insertTableStringBuilder.append("CREATETEMPORARYTABLE").append(tempTableName); 73insertTableStringBuilder.append("ASSELECT*FROM").append(tableName).append(";"); 74db.execSQL(insertTableStringBuilder.toString()); 75printLog("【Table】"+tableName+"\n---Columns-->"+getColumnsStr(daoConfig)); 76printLog("【Generatetemptable】"+tempTableName); 77}catch(SQLExceptione){ 78Log.e(TAG,"【Failedtogeneratetemptable】"+tempTableName,e); 79} 80} 81} 82 83privatestaticbooleanisTableExists(Databasedb,booleanisTemp,StringtableName){ 84if(db==null||TextUtils.isEmpty(tableName)){ 85returnfalse; 86} 87StringdbName=isTemp?SQLITE_TEMP_MASTER:SQLITE_MASTER; 88Stringsql="SELECTCOUNT(*)FROM"+dbName+"WHEREtype=?ANDname=?"; 89Cursorcursor=null; 90intcount=0; 91try{ 92cursor=db.rawQuery(sql,newString[]{"table",tableName}); 93if(cursor==null||!cursor.moveToFirst()){ 94returnfalse; 95} 96count=cursor.getInt(0); 97}catch(Exceptione){ 98e.printStackTrace(); 99}finally{100if(cursor!=null)101cursor.close();102}103returncount>0;104}105106107privatestaticStringgetColumnsStr(DaoConfigdaoConfig){108if(daoConfig==null){109return"nocolumns";110}111StringBuilderbuilder=newStringBuilder();112for(inti=0;i113builder.append(daoConfig.allColumns[i]);114builder.append(",");115}116if(builder.length()>0){117builder.deleteCharAt(builder.length()-1);118}119returnbuilder.toString();120}121122123privatestaticvoiddropAllTables(Databasedb,booleanifExists,@NonNullClassextendsAbstractDao,?>>...daoClasses){124reflectMethod(db,"dropTable",ifExists,daoClasses);125printLog("【Dropalltablebyreflect】");126}127128privatestaticvoidcreateAllTables(Databasedb,booleanifNotExists,@NonNullClassextendsAbstractDao,?>>...daoClasses){129reflectMethod(db,"createTable",ifNotExists,daoClasses);130printLog("【Createalltablebyreflect】");131}132133/**134*daoclassalreadydefinethesqlexecmethod,sojustinvokeit135*/136privatestaticvoidreflectMethod(Databasedb,StringmethodName,booleanisExists,@NonNullClassextendsAbstractDao,?>>...daoClasses){137if(daoClasses.length1){138return;139}140try{141for(Classcls:daoClasses){142Methodmethod=cls.getDeclaredMethod(methodName,Database.class,boolean.class);143method.invoke(null,db,isExists);144}145}catch(NoSuchMethodExceptione){146e.printStackTrace();147}catch(InvocationTargetExceptione){148e.printStackTrace();149}catch(IllegalAccessExceptione){150e.printStackTrace();151}152}153154privatestaticvoidrestoreData(Databasedb,ClassextendsAbstractDao,?>>...daoClasses){155for(inti=0;i156DaoConfigdaoConfig=newDaoConfig(db,daoClasses[i]);157StringtableName=daoConfig.tablename;158StringtempTableName=daoConfig.tablename.concat("_TEMP");159160if(!isTableExists(db,true,tempTableName)){161continue;162}163164try{165//getallcolumnsfromtempTable,takecarefultousethecolumnslist166ListnewTableInfos=TableInfo.getTableInfo(db,tableName);167ListtempTableInfos=TableInfo.getTableInfo(db,tempTableName);168ArrayListselectColumns=newArrayList<>(newTableInfos.size());169ArrayListintoColumns=newArrayList<>(newTableInfos.size());170for(TableInfotableInfo:tempTableInfos){171if(newTableInfos.contains(tableInfo)){172Stringcolumn='`'+tableInfo.name+'`';173intoColumns.add(column);174selectColumns.add(column);175}176}177//NOTNULLcolumnslist178for(TableInfotableInfo:newTableInfos){179if(tableInfo.notnull&&!tempTableInfos.contains(tableInfo)){180Stringcolumn='`'+tableInfo.name+'`';181intoColumns.add(column);182183Stringvalue;184if(tableInfo.dfltValue!=null){185value="'"+tableInfo.dfltValue+"'AS";186}else{187value="''AS";188}189selectColumns.add(value+column);190}191}192193if(intoColumns.size()!=0){194StringBuilderinsertTableStringBuilder=newStringBuilder();195insertTableStringBuilder.append("REPLACEINTO").append(tableName).append("(");196insertTableStringBuilder.append(TextUtils.join(",",intoColumns));197insertTableStringBuilder.append(")SELECT");198insertTableStringBuilder.append(TextUtils.join(",",selectColumns));199insertTableStringBuilder.append("FROM").append(tempTableName).append(";");200db.execSQL(insertTableStringBuilder.toString());201printLog("【Restoredata】to"+tableName);202}203StringBuilderdropTableStringBuilder=newStringBuilder();204dropTableStringBuilder.append("DROPTABLE").append(tempTableName);205db.execSQL(dropTableStringBuilder.toString());206printLog("【Droptemptable】"+tempTableName);207}catch(SQLExceptione){208Log.e(TAG,"【Failedtorestoredatafromtemptable】"+tempTableName,e);209}210}211}212213privatestaticListgetColumns(Databasedb,StringtableName){214Listcolumns=null;215Cursorcursor=null;216try{217cursor=db.rawQuery("SELECT*FROM"+tableName+"limit0",null);218if(null!=cursor&&cursor.getColumnCount()>0){219columns=Arrays.asList(cursor.getColumnNames());220}221}catch(Exceptione){222e.printStackTrace();223}finally{224if(cursor!=null)225cursor.close();226if(null==columns)227columns=newArrayList<>();228}229returncolumns;230}231232privatestaticvoidprintLog(Stringinfo){233if(DEBUG){234Log.d(TAG,info);235}236}237238privatestaticclassTableInfo{239intcid;240Stringname;241Stringtype;242booleannotnull;243StringdfltValue;244booleanpk;245246@Override247publicbooleanequals(Objecto){248returnthis==o249||o!=null250&&getClass()==o.getClass()251&&name.equals(((TableInfo)o).name);252}253254@Override255publicStringtoString(){256return"TableInfo{"+257"cid="+cid+258",name='"+name+'\''+259",type='"+type+'\''+260",notnull="+notnull+261",dfltValue='"+dfltValue+'\''+262",pk="+pk+263'}';264}265266privatestaticListgetTableInfo(Databasedb,StringtableName){267Stringsql="PRAGMAtable_info("+tableName+")";268printLog(sql);269Cursorcursor=db.rawQuery(sql,null);270if(cursor==null)271returnnewArrayList<>();272TableInfotableInfo;273ListtableInfos=newArrayList<>();274while(cursor.moveToNext()){275tableInfo=newTableInfo();276tableInfo.cid=cursor.getInt(0);277tableInfo.name=cursor.getString(1);278tableInfo.type=cursor.getString(2);279tableInfo.notnull=cursor.getInt(3)==1;280tableInfo.dfltValue=cursor.getString(4);281tableInfo.pk=cursor.getInt(5)==1;282tableInfos.add(tableInfo);283// printLog(tableName +":"+ tableInfo);284}285cursor.close();286returntableInfos;287}288}289}

8. GreenDao資料庫加密

開發中對於儲存於資料庫中的敏感資料,我們可以通過對資料庫加密來進行保護。GreenDao可以通過SQLCipher來進行加密處理。下面我們簡單講解下加密過程:

步驟:

  1. 匯入加密庫檔案:

1implementation'net.zetetic:android-database-sqlcipher:3.5.6'
  1. 修改DaoSession的生成方式:

1//MyDaoMasterhelper=newMyDaoMaster(this,"aserbaos.db");//資料庫升級寫法
2DaoMaster.DevOpenHelperhelper=newDaoMaster.DevOpenHelper(this,"aserbao.db");
3//SQLiteDatabasedb=helper.getWritableDatabase();//不加密的寫法
4Databasedb=helper.getEncryptedWritableDb("aserbao");//資料庫加密密碼為“aserbao"的寫法
5DaoMasterdaoMaster=newDaoMaster(db);
6daoSession=daoMaster.newSession();

9. 專案地址

https://github.com/aserbao/AserbaosAndroid
當前文章所有程式碼在AserbaosAndroid/app/src/main/java/com/aserbao/aserbaosandroid/functions/database/greenDao/relation目錄下;(不過就我這脾氣,可能在今後整理程式碼的過程中會修改!不過請放心,修改後會在github上進行說明的)

AserbaosAndroid
aserbao的個人Android總結專案,希望這個專案能成為最全面的Android開發學習專案,這是個美好的願景,專案中還有很多未涉及到的地方,有很多沒有講到的點,希望看到這個專案的朋友,如果你在開發中遇到什麼問題,在這個專案中沒有找到對應的解決辦法,希望你能夠提出來,給我留言或者在專案github地址提issues,我有時間就會更新專案沒有涉及到的部分!專案會一直維護下去。當然,我希望是Aserbao'sAndroid 能為所有Android開發者提供到幫助!也期望更多Android開發者能參與進來,只要你熟悉Android某一塊,都可以將你的程式碼pull上分支!

10 總結

這篇文章寫到這裡,零零碎碎花了差不多兩週時間,從十月八號開始到今天正式準備釋出,也算是對GreenDao資料庫的進一步認識!如文章開頭所說,我Android開發之初,使用的是自己編寫SQLite來實現資料庫儲存,到後來使用第三方儲存LitePal,最近,專案早期就使用了GreenDao,所以就又得學習一番GreenDao。對於開發者來說,我覺得無論是這三種中的哪一種,其實只要掌握一種我覺得就足夠了!當然如果你有時間,可以多學習幾種,多學無害嘛!

11.修改記錄

  1. CreditCard中不能只使用一個useId來做關聯,因為我這裡Teacher和Student都和CreditCard是一對多關係,所以我們需要建兩個對應關係欄位。為了分辨添加了studentId和teacherId。

 [email protected]
2publicclassCreditCard{
3@Id
4Longid;
5LongstudentId;
6LongteacherId;
7StringuserName;//持有者名字
8StringcardNum;//卡號
9StringwhichBank;//哪個銀行的
10intcardType;//卡等級,分類0~5
11}
  1. Student中的@ToMany(referencedJoinProperty =“id”)這個id對應的是CreditCard中的studentId,不是自增Id。(問題由@山豆幾_提出,感謝)
    修改後的程式碼應該是:

1@ToMany(referencedJoinProperty="studentId")
2ListcreditCardsList;

好了,文章到這就結束了。

---END---


創作不易,點個“
在看