最近開發的一個項目主要有兩個特點,這兩點也是在項目開發前需要着重去規划解決方案的:
- 需要和Rest服務端請求大量的數據
- 同時這些數據本地也要保存到sqlite數據庫
對於第一點,目前的Volley、Gson等框架既可以解決從服務端請求數據拉下Json數據並解析成java對象的全過程。但是對於第二點,就有點頭疼了。按照以往的開發模式,我們要寫一些操作sqlite數據庫的代碼,同時可能還需要用到什么查詢數據庫綁定到view上的東西,這里想到了很多Android提供的類:SqliteDataBase、DataHelper、
CursorAdapter或者ContentResolver等等太多了,連我自己都弄不清了,每次做到這里都要去查一些原來的代碼和網上資料
我們知道,那些長期的重復的代碼,必將會被總結成一套簡單的框架。那么對於保存數據庫和取出數據這塊,
是否有更好的更簡單的方式或者框架呢?答案是有的,就是SugarORM框架
SugarORM簡介
要說SugarORM之前不得不說ORM。ORM(Object-Relational Mapping)即對象關系映射模式,是Java開發中常用的技術。 它的作用是在關系型數據庫和業務實體對象之間作一個映射,這樣,我們在具體的操作業務對象的時候,就不需要再去和復雜的SQL語句打交道,只需簡單的操作對象的屬性和方法。因為Android開發也是用Java語言,所以Android平台上涌現了一些Android的ORM框架,比如ORMLite、GreenDao、SugarORM,ORMLite並不是專為Android打造的,GreenDao據稱性能比較高,但是有重大的缺陷,這里后面會說到。所以最后選用了SugarORM這個框架,同時SugarORM一直在更新維護,所以也推薦使用這個框架。
它具有下列優點:
- 不用寫復雜的sql語句,而用簡單的API即可完成創建和操縱數據
- 可以在原有的Bean上僅僅添加小的修改而復用Bean
- 簡化而明了的數據庫設計和創建過程,同時提供表的一對多的支持
SugarORM在github上的官網為:
SugarORM的操作
通過在gradle中添加下列依賴來導入SugarORM:
compile 'com.github.satyan:sugar:1.3'
在你的自定義Application類的onCreate和onTerminate中分別加上
SugarContext.init(this);
SugarContext.terminate();
同時在AndroidManifest.xml中的Application元素中添加下列meta-data:
<applicationandroid:label="@string/app_name"android:icon="@drawable/icon" android:name="com.orm.SugarApp"> . . <meta-dataandroid:name="DATABASE"android:value="sugar_example.db"/> <meta-dataandroid:name="VERSION"android:value="2"/> <meta-dataandroid:name="QUERY_LOG"android:value="true"/> <meta-dataandroid:name="DOMAIN_PACKAGE_NAME"android:value="com.example.bean"/> . . </application>
四個meta-data分別確定了:
- 創建的數據庫db的文件名,將在/data/data/{你的應用包名}/databases下創建對應的文件
- 數據庫版本號
- 是否允許SugarORM記錄log
- 創建數據庫表對應的Bean所在的包的路徑
對於第四點需要強調一些,SugarORM是通過一個Bean文件來創建一個表的,比如你想在sugar_example.db中創建一個叫做Goods的表,那么你需要在上面你規定的com.example.bean中創建一個Goods.java的Bean文件,然后你編譯運行的時候,會自動在db中創建了這個空表
這里具體說明細節:
比如你的Goods.java需要這么寫:
public class Goods extends SugarRecord implements Serializable { /** * 貨品編號 */ @Column(name = "sku_ID", unique = true) @Expose private String skuId; /** * 商品編號 */ @Expose private String spuId; /** * 規格 */ @Expose @Ignore private String specValue; /** * 貨品名稱 */ @Expose private String name; /** * 貨號 */ @Expose private String bn; /** * 成本價,進價 */ @Expose private BigDecimal cost; /** * 售價 */ @Expose private BigDecimal price; public String getSkuId() { return skuId; } public void setSkuId(String skuId) { this.skuId = skuId; } public String getSpuId() { return spuId; } public void setSpuId(String spuId) { this.spuId = spuId; } public String getSpecValue() { return specValue; } public void setSpecValue(String specValue) { this.specValue = specValue; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getBn() { return bn; } public void setBn(String bn) { this.bn = bn; } public BigDecimal getCost() { return cost; } public void setCost(BigDecimal cost) { this.cost = cost; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } }
這是一個描述一個商品的Bean,Sugar會自動的在db中創建Goods這個表,表中的字段和Goods.java中的屬性名對應,這里注意幾點:
- Bean的屬性名所采用駝峰命名法,那么大寫的字母會在創建的表中字段轉換成下划線。比如spuId這個屬性對應的表中的字段名為spu_id
- 之所以實現Serializable是因為這個Bean在代碼中不僅僅為SugarORM創建表而服務,同時也是為了能在Android組件中傳遞(比如Handler中的message.obj)而用,所以這里和官網的直接繼承自SugarRecord<T>不同,推薦大家用我這種方式
- @Column這個注解意思是說你想強制按照你的規定的名字來創建表中對應的字段名字,所以這里的skuId在Goods表中的字段名就不是默認的sku_id了,而是你自己給的sku_ID
- @Expoes是來自於Gson的的一個注解,后面會說到
- @Ignore這個注解強調這個屬性在表中不要創建對應的字段
SugarORM通過save(),delete(),T.findbyid(),T.listAll()等API來簡化數據庫的增刪改查操作:
增加一條數據:
Goods good = new Goods(); good.setName("Coffee"); good.setCost(new BigDecimal(30)); good.setBn("123456"); good.save();
查詢一條數據:
Goods loadGood =Goods.findById(Goods.class,1);
查詢所有的表中的條目:
List<Goods> goods =Goods.listAll(Goods.class);
更新一條數據
:
Goods good2 = Goods.findById(Goods.class, 1); good2.setName("Rice"); good2.save();
刪除一條數據:
Goods good2 = Goods.findById(Goods.class, 1); good2.delete();
刪除表中所有的條目:
Goods.deleteAll(Goods.class);
SugarORM的條件查詢操作
可以直接通過提供的find和findWithQuery進行查詢:
Goods.find(Goods.class, "name = ? and skuId = ?", "Coffee", "123");
如果你有其他的比如groupby、orderby、limit等操作,具體的find的接口格式為:
find(Class<T> type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit)
通過findWithQuery接口查詢:
List<Note> notes = Note.findWithQuery(Note.class, "Select * from Note where name = ?", "satya");
SugarORM同時提供了條件查詢的API,叫做Query Builder,目前還處於Beta版本:
Select.from(TestRecord.class) .where(Condition.prop("test").eq("satya"), Condition.prop("prop").eq(2)) .list();
SugarORM的一對多使用
通常開發中,一個表中的某個字段對應了另一個表,這個在java類中體現的就是一對多的關聯的關系,這里SugarORM也是支持的。比如Goods表中有一個Operator的字段,它說明了負責這個商品的人
public class Operator extends SugarRecord implements Serializable { String userName; String gender; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } }
在Goods.java中可以加上這個Operator的屬性,那么Goods表即也會加上這樣的字段
:
public class Goods extends SugarRecord implements Serializable { ... private Operator operator; public Operator getOperator() { return operator; } public void setOperator(Operator operator) { this.operator = operator; } ... }
下面是查詢的方式:
List<Goods> goods = Goods.find(Goods.class, "operator = ?", new String{operator.getName()});
或者
Goods good = Goods.findById(Goods.class, 1); Operator o = good.getOperator();
SugarORM中Bean可復用Gson的Bean
SugarORM最大的好處是,用於創建表的Bean是你自己可以定義的,這點相對於GreenDao而言是不言而喻的。GreenDao是自動幫你創建了bean類,但是如果這個Bean你又需要用來解析網絡拉下來的Json數據,那么就有問題了。
我們知道Gson也是通過Bean對象來解析Json數據的,但是Gson支持在Bean中的屬性上加一些注解(比如@Expose這個注解)。那么這里你可能想到解析Json所用的bean和儲存數據庫所用的bean能共用,那么這里的存儲數據庫的bean因為需要加一些gson或者別的框架的注解,就不能讓ORM框架來自動生成了。所以, GeenDao沒法使用。
開發中注意的地方
- 命令問題
- 數據庫更改的問題
第一個問題是本文開頭所提到的“
Bean的屬性名所采用駝峰命名法,那么大寫的字母會在創建的表中字段轉換成下划線
”這個問題,從SugarORM公布的源碼里面能看到它是自己加上的"_"而並不是一個bug,這里可能是為了想和java web的一些開發方式保持一致吧,或許是為了規避sqlite對大小寫敏感而導致的各種問題而引入的這種策略。所以開發者在用SugarORM的時候要注意命名的改變。你在Bean中寫了一個屬性名,而數據庫中的字段名並不是這個。當然了,這里可以通過@Column注解來搞定
第二個問題是一個習慣性問題。開發中由於不用直接操作SqliteDataBase這種類型的類了,所以多人同時開發的時候某個人提交代碼的時候只是修改了他所負責的表的對應的Bean的結構,而這個時候你再更新代碼在重新安裝Apk后會發現報錯,比如找不到某個表的字段的錯。這個是因為表結構改了,所以你不得不先卸載原來的應用而重新安裝apk。所以這里最好在多人開發的時候養成一個習慣:某個人修改了某個Bean后(即更新了表結構)一定要通知其他人