Android SQLite框架greenDAO的使用
Android中集成了SQLite資料庫,Android本身提供了完備的SDK提供訪問存取SQLite的介面,通過SQLiteOpenHelper類來管理SQLite資料庫。在SQLiteOpenHelper類中的onCreate方法中建立資料庫,在onUpgrade方法中升級資料庫。
但隨著應用的複雜度越來越高,表設計中的欄位越來越多,我們編寫的SQL語句越來越長,CURD程式碼越來越冗餘冗長,越來越難以維護,而且導致錯誤率上升,影響開發質量和效率。於是各種ORM的資料庫框架應運而生。比如ORMLite、SugarORM、GreenDAO、Active Android、Realm等。其中很多基於註解的ORM實現,類似於Hibernate,但在Android裝置上,從效能上分析並不可取。greenDAO
greenDAO的github地址是https://github.com/greenrobot/greenDAO,官網http://greenrobot.org/greendao。
Android Studio中的整合過程:
1、在module的build.gradle中dependencies的新增
compile ‘de.greenrobot:greendao:2.0.0’
2、匯入greenDAO的程式碼生成器模組:
我採用的方式是直接從github上clone整個greenDAO的專案,然後在自己的專案中匯入需要的程式碼生成器模組,匯入成功後如圖:
3、使用生成器生成Entity,DAO,Master以及Session等greenDAO要用到的類
DaoGenerator和DAOExampleGenerator模組都是Java工程,可以直接右鍵執行,而且DAOExampleGenerator是依賴於DAOGenerator的。我們在DAOExampleGenerator模組中的方法中進行Java物件的生成:
public static void main(String[] args) throws Exception {
Schema schema = new Schema(1000, "com.idengpan.androidwidgte.db" );
addNote(schema);
addCustomerOrder(schema);
new DaoGenerator().generateAll(schema, "E:\\2016\\GithubDemo\\AndroidWidget\\app\\src\\main\\java");
}
private static void addNote(Schema schema) {
Entity note = schema.addEntity("Note");
note.addIdProperty();
note.addStringProperty("text").notNull();
note.addStringProperty("comment");
note.addDateProperty("date");
}
private static void addCustomerOrder(Schema schema) {
Entity customer = schema.addEntity("Customer");
customer.addIdProperty();
customer.addStringProperty("name").notNull();
Entity order = schema.addEntity("Order");
order.setTableName("ORDERS"); // "ORDER" is a reserved keyword
order.addIdProperty();
Property orderDate = order.addDateProperty("date").getProperty();
Property customerId = order.addLongProperty("customerId").notNull().getProperty();
order.addToOne(customer, customerId);
ToMany customerToOrders = customer.addToMany(order, customerId);
customerToOrders.setName("orders");
customerToOrders.orderAsc(orderDate);
}
其中,com.idengpan.androidwidgte.db指這些類生成的包路徑,E:\2016\GithubDemo\AndroidWidget\app\src\main\java指的是生成的類對應的Java檔案的物理磁碟路徑。實際操作過程中,發現它會自動生成包路徑的目錄,將類生成到E:\2016\GithubDemo\AndroidWidget\app\src\main\java\com\idengpan\androidwidgte\db目錄中。生成完,基本上就可以操作了。
注意:這期間,我遇到一個問題,是template not found for name dao.ftl的異常,說是沒找到dao.ftl的模板檔案,其實這個是存在的。後來經過斷點除錯,才發現因為經過了locale後,在中文環境下它會尋找dao_zh_CN.ftl的檔案,自然就有問題了。於是我將dao.ftl複製了一份,改名為dao_zh_CN.ftl,即解決了這個問題,正常生成了檔案。
使用greenDAO
greenDAO操作的都是Java的實體物件,幾乎無需單獨寫完整的SQL程式碼。我在自定義的Appilication中定義方法中定義獲取DAOMaster和DAOSession,這樣防止每次操作都要新建DAOMaster和DAOSession的例項。後面我覺得把Application當做單例類用,不太好,就新建了一個DBService,來獲取DAOMaster和DAOSession。
public class MyApplication extends Application {
private static DaoMaster mDaoMaster;
private static DaoSession mDaoSession;
private static Context mApplicationContext;
@Override
public void onCreate() {
super.onCreate();
mApplicationContext = getApplicationContext();
}
public static DaoMaster getDaoMaster(){
if(mDaoMaster == null){
DaoMaster.OpenHelper openHelper = new DaoMaster.DevOpenHelper(mApplicationContext,"test-db",null);
mDaoMaster = new DaoMaster(openHelper.getWritableDatabase());
}
return mDaoMaster;
}
public static DaoSession getDaoSession(){
if(mDaoSession == null){
if(mDaoMaster == null){
getDaoMaster();
}
mDaoSession = mDaoMaster.newSession();
}
return mDaoSession;
}
}
插入
然後插入例項:
Note note = new Note();
note.setText("這是Note" + index++);
note.setDate(new Date());
note.setComment("這是備註" + index);
MyApplication.getDaoSession().insert(note);
通過看原始碼,發現greenDAO是開了事務的,也就是說如果批量插入1000條資料的話,就會使用1000個事務。這是很不科學的。greenDAO給出的方案是:
MyApplication.getDaoSession().runInTx(new Runnable() {
@Override
public void run() {
long current = System.currentTimeMillis();
for (int k = 0; k < 1000; k++) {
Note note = new Note();
note.setText("這是Note" + k);
note.setDate(new Date());
note.setComment("這是備註" + k);
MyApplication.getDaoSession().insert(note);
}
System.out.println("耗時:" + (System.currentTimeMillis() - current));
}
});
從測試結果是耗時62ms。效率還是很不錯的!!測試幾次,有時可以達到48ms,哈哈。如果不採用在事務中批量插入,而是每次插入都開啟事務,那麼1000條記錄的插入耗時2710ms,平均都在2500ms以上,介面會出現卡頓,效率不高,不可取。
注意:這裡的Runnable是執行在UI執行緒的,如果測試時發現事務耗時較長,需要使用Loader。
查詢
List joes = userDao.queryBuilder()
.where(Properties.FirstName.eq("Joe"))
.orderAsc(Properties.LastName)
.list();
QueryBuilder qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq("Joe"),
qb.or(Properties.YearOfBirth.gt(1970),
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10))));
List youngJoes = qb.list();
更多的內容可以參考greenDAO的原始碼以及官網介紹。注意上述程式碼的符號,qb.or和qb.and是qb.where方法中的引數。
開發測試時可以開啟下面的開關來進行除錯:
QueryBuilder.LOG_SQL = true;
QueryBuilder.LOG_VALUES = true;
刪除
官網是這樣描述的:
Bulk deletes do not delete individual entities, but all entities matching some criteria. To perform bulk deletes, create a QueryBuilder, call its buildDelete method, and execute the returned DeleteQuery. This part of the API may change in the future, e.g. convenience methods may be added etc. Keep in mind, that bulk deletes currently do not affect entities in the identity scope, e.g. you could “resurrect” deleted entities if they have been cached before and are accessed by their ID (load method). Consider clearing the identity scope for now, if that may cause issues for your use case.
呼叫刪除也是使用QueryBuilder,也可以加查詢條件,然後呼叫QueryBuilder的buildDelete方法,然後執行返回的DeleteQuery物件的方法,我看了下目前是executeDeleteWithoutDetachingEntities()方法。
裡面也提到,這個刪除的API未來可能會變更,比如會增加一些更方便使用的API。
於是批量刪除的示例程式碼如下:
QueryBuilder qbDel = DBService.getDaoSession().queryBuilder(Note.class);
qbDel.where(NoteDao.Properties.Id.gt(1000));
DeleteQuery dq = qbDel.buildDelete();
dq.executeDeleteWithoutDetachingEntities();
如果是單個Entity的刪除,就簡單了,可以使用DAO或Session中的方法來執行delete方法,傳入要刪除的Entity即可。
而且它還是按id為key進行刪除的,其它的欄位不校驗。於是使用下面的程式碼就可以刪除id為10的Note物件。
Note note = new Note(10l,"測試delete文字 10","測試delete備註 10",new Date());
DBService.getDaoSession().delete(note);
更新
很簡單:
Note note = new Note(10l,"測試update文字 10","測試update備註 10",new Date());
DBService.getDaoSession().update(note);