最近公司項目選用GreenDao作為Android客戶端本地數(shù)據(jù)庫的對象關系映射框架。對于GreenDao雖然以往也有簡單用過谊惭,但這還是筆者第一次在實際業(yè)務中使用粟害。碰到了題目所述的兩個問題,雖然在Tutorial里和百度沒找到答案哈误,但在官方issue里搜了一圈果然有方案暖眼,遂記錄下來幫助更多人惕耕。
綜合主鍵
需求場景:某張表里需要兩個或多個column組合在一起成為一個綜合主鍵。比如你的表里需要存儲一個用戶的賬單诫肠,雖然賬單也有id司澎,但是你希望一張表存儲所有用戶欺缘,那么就需要把userId和賬單id放在一起作為一個綜合主鍵。雖然sqlite數(shù)據(jù)庫是可以直接指定綜合主鍵的挤安,但不幸的是GreenDao并不支持(筆者也很懵逼為啥不支持)谚殊。
查看官方方案來看一下:
@Entity(
// Define indexes spanning multiple columns here.
indexes = {
@Index(value = "prop1 DESC, prop2 DESC", unique = true)
}
)
public class YourEntity {
@Id(autoincrement = true)
Long id;
Integer prop1;
Long prop2;
...
}
可以看到,它是通過一個加上unique約束的綜合索引來間接實現(xiàn)綜合主鍵的需求蛤铜。這樣的話還是要用@Id注解定義一個主鍵嫩絮,但是我們完全不用管理這個主鍵,后面插入數(shù)據(jù)的適合這個字段直接插入null即可围肥。真正的主鍵是prop1剿干,prop2這兩個組合的綜合主鍵,使用insertOrReplace方法插入數(shù)據(jù)時穆刻,如果這兩個值完全相等的話置尔,就會替換原有數(shù)據(jù)。不放心的話大家可以試一試氢伟,筆者已經試過了榜轿。
存儲自定義類型對象
需求場景:sqlite數(shù)據(jù)庫只能直接存儲數(shù)字、字符串腐芍、日期等簡單類型差导,如果要存儲一個復雜對象的話需要把對象拆解為一個個簡單數(shù)據(jù)類型,畢竟再復雜的數(shù)據(jù)類型也是由簡單數(shù)據(jù)類型組合而來猪勇。本以為大名鼎鼎的GreenDao可以幫我們智能拆解、組裝對象颠蕴,結果搜了一圈竟然找不到沒找到存儲自定義類型的辦法泣刹。
好在在官方文檔上找到解決方案:
@Entity
public class User {
@Id
private Long id;
@Convert(converter = RoleConverter.class, dbType = Integer.class)
private Role role;
public enum Role {
DEFAULT(0), AUTHOR(1), ADMIN(2);
final int id;
Role(int id) {
this.id = id;
}
}
public static class RoleConverter implements PropertyConverter<Role, Integer> {
@Override
public Role convertToEntityProperty(Integer databaseValue) {
if (databaseValue == null) {
return null;
}
for (Role role : Role.values()) {
if (role.id == databaseValue) {
return role;
}
}
return Role.DEFAULT;
}
@Override
public Integer convertToDatabaseValue(Role entityProperty) {
return entityProperty == null ? null : entityProperty.id;
}
}
}
可以看到這個實體類里包含了一個自定義的枚舉類型Role,在該類型上加了一個@Convert注解犀被,括號里面指定了用于轉換對象類型和數(shù)據(jù)庫類型的converter類椅您,以及該對象存儲在數(shù)據(jù)庫中的類型。
再來看看這個converter類做了什么事情寡键。很簡單掀泳,它實現(xiàn)了PropertyConverter接口,里面有兩個方法西轩,convertToEntityProperty是將數(shù)據(jù)庫中的類型轉換為java實體類员舵;convertToDatabaseValue方法相反,將java實體類轉換為數(shù)據(jù)庫中的類型藕畔。我們只要在這兩個方法里定義相應的轉換規(guī)則即可马僻。
看上去也不難,思路也很清晰注服。但是這個例子里的Enum類型要轉換為int類型還是比較簡單的韭邓,而筆者要存儲的對象要復雜的多措近。這還是解決不了我的需求啊(欲哭無淚)女淑。
如何快速簡單的把一個復雜的數(shù)據(jù)類型轉換為簡單數(shù)據(jù)類型瞭郑,而且還要能精準地轉換回來?好像是有這么一個東西鸭你,jsonG拧!苇本!json作為客戶端和服務端之間數(shù)據(jù)傳遞的載體袜茧,確實滿足我們現(xiàn)在的業(yè)務需求。更棒的是我們有gson這個解析框架來幫我們做轉換瓣窄!那么我們要做的事真就是無腦操作了笛厦。來看看我的Demo代碼:
@Entity(
)
public class Zoo {
indexes = {
@Index(value = "zooId DESC, zoneId DESC", unique = true)
}
@Id(autoincrement = true)
private Long id;
@Property
private Long zooId;
@Property
private Long zoneId;
@Property
@Convert(converter = CatConverter.class, columnType = String.class)
private Cat cat;
public static class CatConverter implements PropertyConverter<Cat, String> {
@Override
public Cat convertToEntityProperty(String databaseValue) {
if (databaseValue == null) {
return null;
}
return new Gson().fromJson(databaseValue, Cat.class);
}
@Override
public String convertToDatabaseValue(Cat entityProperty) {
if (entityProperty == null) {
return null;
}
return new Gson().toJson(entityProperty);
}
}
}
這里我新建了一個叫Zoo的實體,里面包含一個Cat類型的對象俺夕,且不管該對象復雜與簡單裳凸,我們甚至都不需要關心它的具體數(shù)據(jù)結構。加上@Convert注解后新建一個CatConverter類(注意converter類是內部類的話要聲明為static)劝贸,因為我們要轉換為json存儲起來所以數(shù)據(jù)庫中的類型肯定是String了姨谷,標注好泛型,做一個參數(shù)的非空判斷(良好習慣)映九。在轉換的方法內部我們只需要利用gson將數(shù)據(jù)在java bean類型和json之間轉換梦湘,就可以完成我們的需求了,是不是很簡單呢件甥?
Cat miaomiao = new Cat(13, "miaomiao");
Cat jingjing = new Cat(13, "jingjing");
ZooDao zooDao = daoSession.getZooDao();
zooDao.insertOrReplace(new Zoo(null, 222L, 333L, miaomiao));
zooDao.insertOrReplace(new Zoo(null, 222L, 331L, jingjing));
List<Zoo> zoos = zooDao.queryBuilder().list();
for (Zoo zoo : zoos) {
Log.d("xxx", zoo.getZooId()+":"+zoo.getZoneId()+":"+zoo.getCat().toString());
}
寫完代碼后make project自動生成新的ZooDao類(有興趣的可以看看這個類捌议,其實也挺簡單的),不放心趕緊實驗一下能不能直接存取自定義對象了引有。
大家有什么問題的話可以留言瓣颅,歡迎交流~