入坑前的鋪墊
昨天在上一篇[GreenDao項(xiàng)目接入]http://www.reibang.com/p/dac3bd9bad72 中說明了在項(xiàng)目中使用GreenDao的一個(gè)流程,因?yàn)闀r(shí)間問題沒有詳細(xì)的講解有關(guān)增刪改查的一些問題,今天給大家補(bǔ)充一下巩踏。
public class User extends BaseBean
{
private int memberSex;//性別
private String memberLastX;//X幣
private String memberNickname;//昵稱
private String memberIcon;//頭像地址鏈接
private String memberMobile;//手機(jī)號(hào)
private int memberId;//用戶ID
private String memberDetailAddr;//用戶的詳細(xì)地址
private String memberLastExperience;//用戶經(jīng)驗(yàn)值
private String memberLevelName;//用戶等級(jí)昵稱
private long memberBirthday;//用戶生日
private String memberProvince;//用戶所在地
}
上面為我實(shí)際開發(fā)中用來獲取接口用戶信息的實(shí)體類,然后我按照步驟對(duì)實(shí)體類進(jìn)行了注釋肥矢,如下:
@Entity
public class User extends BaseBean
{
@Id
private Long id;
private int memberSex;//性別
private String memberLastX;//X幣
private String memberNickname;//昵稱
private String memberIcon;//頭像地址鏈接
private String memberMobile;//手機(jī)號(hào)
private int memberId;//用戶ID
private String memberDetailAddr;//用戶的詳細(xì)地址
private String memberLastExperience;//用戶經(jīng)驗(yàn)值
private String memberLevelName;//用戶等級(jí)昵稱
private long memberBirthday;//用戶生日
private String memberProvince;//用戶所在地
}
不知道大家有沒有仔細(xì)觀察,我在實(shí)體類里面又定義了一個(gè)Long類型的字段作為ID(是按照官方Demo中開發(fā)的)叠洗,正是這個(gè)ID甘改,讓我掉了一次坑,給大家詳細(xì)的描述下當(dāng)時(shí)我遇到的問題以及解決辦法:
我按照上面的實(shí)體類去Make project生成對(duì)應(yīng)的UserDao和DaoMaster灭抑、DaoSession十艾。然后我在代碼里面緩存用戶信息的時(shí)候是這么做的,首先從服務(wù)器獲取用戶數(shù)據(jù)腾节,然后轉(zhuǎn)換成一個(gè)User對(duì)象使用GreenDao進(jìn)行本地緩存忘嫉,代碼如下:
/**
* 緩存用戶信息
*
* @param user
*/
public void cacheUserInfo(User user)
{
UserDao userDao = GreenDaoManager.getInstance().getmDaoSession().getUserDao();
userDao.save(user);
}
GreenDao里面提供存儲(chǔ)數(shù)據(jù)的方法有三個(gè):
-
save(T entity)
"Saves" an entity to the database: depending on the existence of the key property, it will be inserted (key is null) or updated (key is not null).通過key屬性判斷是否存在,如果存在就更新數(shù)據(jù)案腺,如果key為null就插入庆冕。這里的key是什么東東?這就是我今天被坑的地方劈榨,這個(gè)key就是我新增的Long id這個(gè)字段访递,也就是表中的主鍵 -
insert(T entity)
Insert an entity into the table associated with a concrete DAO.
插入實(shí)體類,這個(gè)是直接插入同辣。 -
insertOrReplace(T entity)
Insert an entity into the table associated with a concrete DAO.將實(shí)體插入到與具體DAO關(guān)聯(lián)的表中拷姿,這是官方api的給的解釋,不過他的實(shí)際效果和svae很類似邑闺,就是如果存在就修改跌前,不存在插入棕兼。
這里先說一下查詢的方法吧陡舅,官方提供查詢的方法有:
- loadByRowId(long rowId)
-
load(K key)
Loads the entity for the given PK.
根據(jù)主鍵獲取對(duì)象,也就是通過id獲取
但是我只有實(shí)體類的memberId伴挚,也就是在服務(wù)器上生成的Id靶衍,所以肯定沒法用這兩個(gè)方法獲取到數(shù)據(jù)灾炭,然后又查文檔,發(fā)現(xiàn)[QueryBuilder<T>]http://greenrobot.org/files/greendao/javadoc/current/ ,在QueryBuilder中提供了幾個(gè)方法:
-
unique()
Shorthand for build()
.
Shorthand for build()
.unique()
; see Query.unique()
for details. To execute a query more than once, you should build the query and keep the Query
object for efficiency reasons.
大概的意思就是說通過build對(duì)象構(gòu)建一個(gè)Query<T>的對(duì)象颅眶,可以重復(fù)反復(fù)的利用這個(gè)去進(jìn)行獲取數(shù)據(jù)
入坑ing
我是怎么入坑的蜈出? 當(dāng)時(shí)我看了文檔之后發(fā)現(xiàn)save挺智能的嘛,就尋思著用save吧涛酗,自身就是在插入前做個(gè)判斷铡原,省時(shí)省力,然后就有了上面的那段代碼商叹,然后我在就在個(gè)人中心去獲取緩存的數(shù)據(jù)燕刻,方法如下:
/**
* 從本地緩存中獲取user對(duì)象
*
* @param memberId 用戶ID
* @return 返回用戶信息
*/
public User getUserInfoFromCache(String memberId)
{
UserDao userDao = GreenDaoManager.getInstance().getmDaoSession().getUserDao();
Query<User> query = userDao.queryBuilder().where(UserDao.Properties.MemberId.eq(memberId))
.orderDesc(UserDao.Properties.MemberId).build();
return query.unique();
}
看起來沒有什么問題,然后我就去跑程序測(cè)試剖笙,第一次發(fā)現(xiàn)吆喝卵洗,果然好使,執(zhí)行完后數(shù)據(jù)庫果然有了一行數(shù)據(jù)弥咪,屁顛的退出再來一遍过蹂,竟然崩了,一看日志提示:Expected unique result, but count was 2聚至,意思就是說unique只能查詢數(shù)據(jù)庫中有一條符合條件的數(shù)據(jù)酷勺,上面的方法中我的查詢條件是.where(UserDao.Properties.MemberId.eq(memberId)),也就是memberId等于當(dāng)前用戶的晚岭,打印輸出果然數(shù)據(jù)庫有兩條數(shù)據(jù)鸥印,然后找原因,肯定是在緩存的方法中去找坦报,發(fā)現(xiàn)save也沒什么問題啊库说,想不通的時(shí)候必殺絕招就是看源碼
出坑
save的源碼如下:
public void save(T entity) {
if (hasKey(entity)) {
update(entity);
} else {
insert(entity);
}
}
大家可以看到其實(shí)他就是update和insert的一個(gè)判斷使用,有個(gè)hasKey的方法片择,然后查看hasKey的源碼:
abstract protected boolean hasKey(T entity);
發(fā)現(xiàn)是一個(gè)抽象方法潜的,那我只能去他的繼承類UserDao里面去找了
@Override
public boolean hasKey(User entity)
{
return entity.getId() != null;
}
終于找到了真兇,沒錯(cuò) 字管,就是我們定義的Long id這個(gè)字段啰挪,原來他在插入之前是判斷了這個(gè)是否存在,如果不存在就執(zhí)行插入嘲叔,如果存在就更新亡呵,然后就明白了,我在插入數(shù)據(jù)的時(shí)候是從服務(wù)器獲取到的數(shù)據(jù)硫戈,只有memberId锰什,而沒有本地?cái)?shù)據(jù)庫的id,所以第二次緩存的時(shí)候仍然是執(zhí)行了insert的操作,然后在查詢的時(shí)候肯定出現(xiàn)了問題汁胆,真相大白就可以想辦法解決了梭姓。下面是我的解決代碼:
/**
* 緩存用戶信息
*
* @param user
*/
public void cacheUserInfo(User user)
{
User oldUser = getUserInfoFromCache(user.getMemberId() + "");
if (oldUser != null)
{
user.setId(oldUser.getId());
}
UserDao userDao = GreenDaoManager.getInstance().getmDaoSession().getUserDao();
userDao.save(user);
}
在插入之前坐了個(gè)判斷,判斷是否有memberId相同的存在嫩码,如果存在取出GreenDao對(duì)應(yīng)的id誉尖,放入user,這樣在hasKey判斷的時(shí)候肯定是執(zhí)行update了铸题。ok铡恕,問題解決。不過丢间,還有一種可能没咙,說可不可能不添加Long id這個(gè)字段,直接給我們的memberId添加@Id呢千劈,實(shí)際上試試不行的祭刚。大家看下面修改之后的代碼就知道了:
@Entity
public class User extends BaseBean
{
private int memberSex;//性別
private String memberLastX;//X幣
private String memberNickname;//昵稱
private String memberIcon;//頭像地址鏈接
private String memberMobile;//手機(jī)號(hào)
@Id
private int memberId;//用戶ID
private String memberDetailAddr;//用戶的詳細(xì)地址
private String memberLastExperience;//用戶經(jīng)驗(yàn)值
private String memberLevelName;//用戶等級(jí)昵稱
private long memberBirthday;//用戶生日
private String memberProvince;//用戶所在地
}
然后Make project,執(zhí)行了下出現(xiàn)問題了墙牌,然后去查看hasKey的代碼:
@Override
public boolean hasKey(User entity) {
throw new UnsupportedOperationException("Unsupported for entities with a non-null key");
}
變成了直接拋出異常了涡驮,所以,這個(gè)想法大家就別想了喜滨,如果有更好的辦法歡迎大家下方留言告知捉捅。謝謝。
我每天都會(huì)更新一個(gè)小知識(shí)給大家虽风,如果有興趣可以關(guān)注棒口,指不定什么時(shí)候就可以幫到你了,謝謝大家辜膝。