GreenDao存儲自定義類型對象解決方案(轉)


最近公司項目選用GreenDao作為Android客戶端本地數據庫的對象關系映射框架。對於GreenDao雖然以往也有簡單用過,但這還是筆者第一次在實際業務中使用。碰到了題目所述的兩個問題,雖然在Tutorial里和百度沒找到答案,但在官方issue里搜了一圈果然有方案,遂記錄下來幫助更多人。

綜合主鍵

需求場景:某張表里需要兩個或多個column組合在一起成為一個綜合主鍵。比如你的表里需要存儲一個用戶的賬單,雖然賬單也有id,但是你希望一張表存儲所有用戶,那么就需要把userId和賬單id放在一起作為一個綜合主鍵。雖然sqlite數據庫是可以直接指定綜合主鍵的,但不幸的是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約束的綜合索引來間接實現綜合主鍵的需求。這樣的話還是要用@Id注解定義一個主鍵,但是我們完全不用管理這個主鍵,后面插入數據的適合這個字段直接插入null即可。真正的主鍵是prop1,prop2這兩個組合的綜合主鍵,使用insertOrReplace方法插入數據時,如果這兩個值完全相等的話,就會替換原有數據。不放心的話大家可以試一試,筆者已經試過了。

存儲自定義類型對象

需求場景:sqlite數據庫只能直接存儲數字、字符串、日期等簡單類型,如果要存儲一個復雜對象的話需要把對象拆解為一個個簡單數據類型,畢竟再復雜的數據類型也是由簡單數據類型組合而來。本以為大名鼎鼎的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注解,括號里面指定了用於轉換對象類型和數據庫類型的converter類,以及該對象存儲在數據庫中的類型。

再來看看這個converter類做了什么事情。很簡單,它實現了PropertyConverter接口,里面有兩個方法,convertToEntityProperty是將數據庫中的類型轉換為java實體類;convertToDatabaseValue方法相反,將java實體類轉換為數據庫中的類型。我們只要在這兩個方法里定義相應的轉換規則即可。

看上去也不難,思路也很清晰。但是這個例子里的Enum類型要轉換為int類型還是比較簡單的,而筆者要存儲的對象要復雜的多。這還是解決不了我的需求啊(欲哭無淚)。

如何快速簡單的把一個復雜的數據類型轉換為簡單數據類型,而且還要能精准地轉換回來?好像是有這么一個東西,json!!!json作為客戶端和服務端之間數據傳遞的載體,確實滿足我們現在的業務需求。更棒的是我們有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類型的對象,且不管該對象復雜與簡單,我們甚至都不需要關心它的具體數據結構。加上@Convert注解后新建一個CatConverter類(注意converter類是內部類的話要聲明為static),因為我們要轉換為json存儲起來所以數據庫中的類型肯定是String了,標注好泛型,做一個參數的非空判斷(良好習慣)。在轉換的方法內部我們只需要利用gson將數據在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類(有興趣的可以看看這個類,其實也挺簡單的),不放心趕緊實驗一下能不能直接存取自定義對象了。



作者:業松
鏈接:https://www.jianshu.com/p/513fb2ba5485
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM