GreenDAO簡介
在Android項目開發(fā)過程中弯淘,sqlite來保存數據禾蚕。由于原生api用起來比較麻煩,所以現在興起很多的開源ORM框架勉失。所以總的來說GreenDAO就是一款非常高效易用的ORM框架GitHub地址。相比于其他框架主要有以下特點:
- 性能高效
- 簡潔的API
- 為Android做了很多優(yōu)化
- 內存消耗小
網上有哥們兒專門做了性能對比實驗皂林,大家可以看看(http://www.tuicool.com/articles/7VVfye)
其實GreenDAO已經出來很久朗鸠,但是之前的版本用起來真的非常麻煩。項目中集成起來不方便础倍,而且學習成本相對較高烛占。但是前端時間升級到3.0之后,用起來可以說得上是“傻瓜式”沟启,下面就讓我們開始玩玩GreenDAO3.0忆家。
集成GreenDAO
首先需要在project的gradle文件中引入greenDAO插件:
然后在module的gradle文件中添加greenDAO的插件,并引入相關類庫:
Gradle 插件配置:
- schemaVersion: 數據庫schema版本德迹,也可以理解為數據庫版本號芽卿;
- daoPackage:設置DaoMaster 、DaoSession胳搞、Dao包名卸例;
- targetGenDir:設置DaoMaster 、DaoSession肌毅、Dao目錄筷转;
- targetGenDirTest:設置生成單元測試目錄;
- generateTests:設置自動生成單元測試用例悬而。
在這里需要注意首先引入的是引入相關插件呜舒,然后在dependencies中引入greenDAO的類庫。其中schemaVersion表示數據庫版本號笨奠,每次數據庫升級的時候我們修改這里的版本號即可(修改這里的版本號袭蝗,greenDAO會自動修改生成到DAOMaster中的版本號),targetGenDir表示greenDAO生成的DAOMaster和DaoSession的位置艰躺。做完這一切我們就已經成功將greenDAO引入到我們的項目中了呻袭。
使用GreenDAO
數據庫引入成功后眨八,在使用之前腺兴,我們還得先來創(chuàng)建一個實體類:
@Entity表示這個實體類一會會在數據庫中生成對應的表,@Id表示該字段是id廉侧,小伙伴們注意該字段的數據類型為包裝類型Long页响,為什么要用包裝類型呢?我們一會插入數據的時候再說段誊。@Property則表示該屬性將作為表的一個字段闰蚕,其中nameInDb看名字就知道這個屬性在數據庫中對應的數據名稱。OK连舍,寫完這些之后將項目進行編譯没陡,編譯成功之后系統(tǒng)會幫助我們生成相應的構造方法和get/set方法,并且還會在我們的包下生成DaoMaster和DaoSession。
接下來我們就可以進行數據庫的初始化了(官方推薦在Application下面做):
public class MyApplication extends Application {
private DaoSession daoSession;
@Override
public void onCreate() {
super.onCreate();
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this,"test.db",null);
Database db = helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
}
public DaoSession getDaoSession() {
return daoSession;
}
}
首先獲取一個DevOpenHelper對象盼玄,這個類有點類似于我們使用的SqliteOpenHelper贴彼,我們主要在這個類中對數據庫的版本進行管理。同時我們在這里提供一個借口用來提供DaoSession對象埃儿。因為我們需要用DAOSession來獲取DAO對象器仗,然后使用DAO對象操作數據庫。
數據庫操作CRUD
數據庫的增刪改查我們都將通過UserDao來進行童番,所以我們需要先對UserDao進行一個初始化:
daoSessin = ((MyApplication) getApplication()).getDaoSession();
userDao = daoSessin.getUserDao();
添加數據
User addUser = new User();
addUser.setUsername("xiaoming");
addUser.setNickname("angle");
userDao.insert(addUser);
修改數據
修改數據需要先執(zhí)行查詢精钮,將待修改的數據查詢出來,然后再分別執(zhí)行修改:
List<User> updateList = userDao.queryBuilder().where(UserDao.Properties.Username.eq("tianrenzheng")).build().list();
for (User u : updateList) {
u.setNickname("updated angle");
userDao.update(u);
}
刪除數據
刪除數據原理同修改數據類似:
List<User> deleteList = userDao.queryBuilder().where(UserDao.Properties.Username.eq("tianrenzheng")).build().list();
for (User u : deleteList) {
userDao.delete(u);
}
查詢數據
由上面的這個操作剃斧,可以看出查詢操作在GreenDAO使用過程中的重要性轨香,因為修改和刪除數據都需要先將數據查詢出來才能操作。所以理解和熟悉查詢語句是GreenDAO日常使用中重點:
Query<User> userQuery = userDao.queryBuilder().orderAsc(UserDao.Properties.Id).build();
List<User> users = userQuery.list();
for (User u : users) {
Log.d(TAG, u.toString());
}
這里只是一個簡單的例子悯衬,SQL語句的所有操作弹沽,我們可以通過構建Query對象來實現策橘。這里大家在實踐中去摸索买决,函數的命名跟SQL關鍵字都是類似的。
數據庫升級
數據庫的升級其實就兩個步驟我們來看看:
1.修改gradle文件
首先在module的gradle文件中修改版本號:
2.修改實體類
@Entity
public class User {
@Property
private int age;
@Property
private String password;
@Id
private Long id;
@Property(nameInDb = "USERNAME")
private String username;
@Property(nameInDb = "NICKNAME")
private String nickname;
}
重新編譯項目運行即可没卸。一般的數據庫升級這樣就可以了,特殊情況可能需要自己編寫數據庫遷移腳本细卧,這種時候可以自定義DBHelper赋铝,定義方式如下,注意繼承類:
public class DBHelper extends DaoMaster.OpenHelper {
public static final String DBNAME = "lenve.db";
public DBHelper(Context context) {
super(context, DBNAME, null);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
super.onUpgrade(db, oldVersion, newVersion);
}
}
可以在onUpgrade方法中進行數據庫的遷移筑凫,如果自定義了DBHelper,則數據庫的初始化變?yōu)槿缦路绞剑?/p>
DBHelper devOpenHelper = new DBHelper(this);
DaoMaster daoMaster = new DaoMaster(devOpenHelper.getWritableDb());
DaoSession daoSession = daoMaster.newSession();
userDao = daoSession.getUserDao();
多表關聯(lián)
數據庫的表可能涉及到一對一和一對多關系丸边,在greenDAO中涉及到to-one和to-many關系骄呼。例如哆致,你想在greenDAO中建立一個對多關系模型臣咖,你就 需要使用to-one和to-many關系。但是夺蛇,請注意疚漆,to-one和to-many關系是不相關的,所以你必須同時更新刁赦。
1:1關聯(lián)
當我們在使用sqlite數據庫來實現表的1:1關聯(lián)時娶聘,通常我們會在主表中定義一個外鍵去關聯(lián)副表甚脉,當要查詢對應的數據時丸升,首先我們要知道查詢數據的外鍵,然后需要用外鍵去副表中查詢所需要的數據牺氨。比如下面這樣:
@Entity
public class Customer {
private Long id; private String username;
}
@Entity
public class Order {
private Long orderId;
private String orderInfo;
private Long customerId;
}
Customer表通過orderId與Order表關聯(lián)狡耻,查詢Order的Customer時需要先知道Order中的customerId然后根據
Customer.id=Order .customerId值再去數據庫中查詢該orderId所對應的Customer對象。然而在greenDao中一個注釋就可以搞定猴凹,只需要使用@ToOne注釋來定義一個關聯(lián)對象即可夷狰。
- @ToOne 定義了一個entities與另一個entities的1:1對應關系。通過joinProperty參數來定義一個外鍵下面是代碼示例:
@Entity
public class Customer {
@Id(autoincrement = true)
@Property(nameInDb = "CUSTOMERID")
private Long customerId;
@Property(nameInDb = "USERNAME")
private String username;
}
@Entity
public class Order {
@Id(autoincrement = true)
@Property(nameInDb = "ORDERID")
private Long orderId;
@Property(nameInDb = "ORDERINFO")
private String orderInfo;
@Property(nameInDb = "CUSTOMERID")
private Long customerId;
@ToOne(joinProperty = "customerId")
private Customer customer;
}
這樣只要獲得Order對象就能通過getCustomer()方法獲取Order所對應的Customer了郊霎,這樣是不是很高效孵淘,很簡便。其實getCustomer方法也很簡單歹篓,就是在底層幫助我們封裝好了查詢語句而已瘫证,另外getCustomer獲取的對象也是懶查詢機制,只有真正使用getCustomer方法查詢到的對象時greenDao才會執(zhí)行查詢操作庄撮。如果你想立即執(zhí)行查詢操作可以調用DAO類的loadDeep()與queryDeep()方法背捌。如果編譯的時候發(fā)生以下問題是因為沒有設置主鍵(@Id):
![沒有設置主鍵 . . .]
1:N 關聯(lián)
在1對1關聯(lián)中每個顧客只能與一個訂單對應,但是現實生活中肯定不只是這樣洞斯,也會出現一個顧客下多個訂單的情況毡庆。如果出現這種需求的話,按照原生Sqlite的思路一樣是通過外鍵關聯(lián)即可烙如,只是這一次查詢的對象會有很多個么抗,但是使用greenDao的1:1關聯(lián)方式顯然不行。不過別擔心greenDao還給我們準備了@ToMany注釋亚铁。
- @ToMany 定義了一個entities(這個標記為源實體)與另一個entities(這個標記為目標實體)的多個對象的關聯(lián)關系:@Tomany有一下三種方式來定義1:N的映射關系蝇刀。
- referencedJoinProperty 在目標實體中我們需要定義一個與源實體關聯(lián)起來的外鍵,即Order中的customerId徘溢,然后需要在源實體里我們需要將customerId作為referencedJoinProperty的屬性吞琐。
public class Customer {
@Id(autoincrement = true)
@Property(nameInDb = "CUSTOMERID")
private Long customerId;
@Property(nameInDb = "USERNAME")
private String username;
@ToMany(referencedJoinProperty = "customerId")
private List<Order> orders;
}
@Entity
public class Order {
@Id(autoincrement = true)
@Property(nameInDb = "ORDERID")
private Long orderId;
@Property(nameInDb = "ORDERINFO")
private String orderInfo;
@Property(nameInDb = "CUSTOMERID")
private Long customerId;
}
- joinProperties這個參數是referencedJoinProperty 參數的升級版捆探。在referencedJoinProperty參數中我們發(fā)現倆個實體關聯(lián)的外鍵是CustomerId與id,但是如果我們的需求是外鍵不能通過id來定義站粟,需要用自己自定義屬性來定義黍图,第一種方法就沒法用了,而joinProperties就是為了解決這個需求的奴烙。
@Entity
public class Customer {
@Id private Long id;
@Unique private String tag;
@ToMany(joinProperties = {
@JoinProperty(name = "tag", referencedName = "customerTag")
})
@OrderBy("date ASC")
private List<Site> orders;
}
@Entity
public class Order {
@Id private Long id;
private Date date;
@NotNull private String customerTag;
}
其實如果把
@ToMany(joinProperties = {
@JoinProperty(name = "id", referencedName = "customerId")
})
這樣的話就和第一種方法實現原理是一樣的了助被。
N:M關系
- @JoinEntity 定義了N:M的映射關系。
@Entity
public class Product {
@Id private Long id;
@ToMany
@JoinEntity(
entity = JoinProductsWithOrders.class,
sourceProperty = "productId",
targetProperty = "orderId"
)
private List<Order> ordersWithThisProduct;
}
@Entity
public class JoinProductsWithOrders {
@Id private Long id;
private Long productId;
private Long orderId;
}
@Entity
public class Order {
@Id private Long id;
}
關聯(lián)表的更新與解析
關聯(lián)的查詢也是懶加載機制切诀,而且查詢的結果會保存在緩存中下一次查詢的時候如果緩存有會直接從緩存中獲取結果恰起。
同樣關聯(lián)表更新時因為有緩存機制的存在你需要將改動的表手動的通過add()方法來更新關聯(lián)表中的對象或者直接清除緩存。
// 獲取當前顧客的訂單列表
List<Order> orders1 = customer.getOrders();
// 插入一個新訂單
Order order = new Order();
order.setCustomerId(customer.getId());
daoSession.insert(order);
// 再一次獲取顧客的訂單
List<Order> orders2 = customer.getOrders();
// 因為緩存列表沒有更新所以訂單1與訂單2的大小相等
assert(orders1.size() == orders2.size);
// 也是相同的對象
assert(orders1.equals(orders2));
//調用該方法后趾牧,才能更新緩存列表
orders1.add(newOrder);
//刪除時也許要手動將緩存列表里面的數據刪除
List orders = customer.getOrders();
// 從數據庫中移除
daoSession.delete(someOrder);
// 手動從緩存列表移除
orders.remove(someOrder);
//如果數據庫更新后不想手動添加數據可以使用resetXX()方法來清除緩存
customer.resetOrders();
List orders = customer.getOrders();
多表查詢
有些時候我們的表沒有使用ToOne與ToMany建立關聯(lián)關系检盼,但是我們又想一步到位。這時我們可以使用greenDao的多表查詢功能來幫助我們減少不必要的代碼翘单。
關聯(lián)單個表
//查詢地址是住在迪拜大樓的用戶
QueryBuilder<User> queryBuilder = userDao.queryBuilder();
queryBuilder.join(Address.class, AddressDao.Properties.userId)
.where(AddressDao.Properties.Street.eq("迪拜大樓"));
List<User> users = queryBuilder.list();
關聯(lián)多個表
//查詢在歐洲人口超過100000的城市
QueryBuilder qb = cityDao.queryBuilder().where(Properties.Population.ge(1000000));
Join country = qb.join(Properties.CountryId, Country.class);
Join continent = qb.join(country, CountryDao.Properties.ContinentId,
Continent.class, ContinentDao.Properties.Id);
continent.where(ContinentDao.Properties.Name.eq("Europe"));
List<City> bigEuropeanCities = qb.list();
通過queryBuilder.join()鏈式調用來實現多表查詢
注意:多表查詢的前提是我們已經定義好了外鍵來關聯(lián)表與表之間的關系吨枉。
注解(不全,用到了再補充)
實體@Entity注解
- schema:告知GreenDao當前實體屬于哪個schema哄芜;
- active:標記一個實體處于活動狀態(tài)貌亭,活動實體有更新、刪除和刷新方法认臊;
- nameInDb:在數據中使用的別名圃庭,默認使用的是實體的類名;
- indexes:定義索引失晴,可以跨越多個列;
- createInDb:標記創(chuàng)建數據庫表;
基礎屬性注解
- @Id :主鍵 Long型剧腻,可以通過@Id(autoincrement = true)設置自增長;
- @Property:設置一個非默認關系映射所對應的列名涂屁,默認是的使用字段名 舉例:@Property (nameInDb="name")书在;
- @NotNul:設置數據庫表當前列不能為空;
- @Transient :添加次標記之后不會生成數據庫表的列拆又;
索引注解
- @Index:使用@Index作為一個屬性來創(chuàng)建一個索引儒旬,通過name設置索引別名,也可以通過unique給索引添加約束帖族;
- @Unique:向數據庫列添加了一個唯一的約束栈源;
關系注解
- @ToOne:定義與另一個實體(一個實體對象)的關系
- @ToMany:定義與多個實體對象的關系