入坑前的鋪墊
昨天在上一篇[GreenDao項目接入]http://www.lxweimin.com/p/dac3bd9bad72 中說明了在項目中使用GreenDao的一個流程,因為時間問題沒有詳細的講解有關增刪改查的一些問題,今天給大家補充一下。
public class User extends BaseBean
{
private int memberSex;//性別
private String memberLastX;//X幣
private String memberNickname;//昵稱
private String memberIcon;//頭像地址鏈接
private String memberMobile;//手機號
private int memberId;//用戶ID
private String memberDetailAddr;//用戶的詳細地址
private String memberLastExperience;//用戶經驗值
private String memberLevelName;//用戶等級昵稱
private long memberBirthday;//用戶生日
private String memberProvince;//用戶所在地
}
上面為我實際開發(fā)中用來獲取接口用戶信息的實體類,然后我按照步驟對實體類進行了注釋,如下:
@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;//手機號
private int memberId;//用戶ID
private String memberDetailAddr;//用戶的詳細地址
private String memberLastExperience;//用戶經驗值
private String memberLevelName;//用戶等級昵稱
private long memberBirthday;//用戶生日
private String memberProvince;//用戶所在地
}
不知道大家有沒有仔細觀察,我在實體類里面又定義了一個Long類型的字段作為ID(是按照官方Demo中開發(fā)的),正是這個ID,讓我掉了一次坑,給大家詳細的描述下當時我遇到的問題以及解決辦法:
我按照上面的實體類去Make project生成對應的UserDao和DaoMaster、DaoSession。然后我在代碼里面緩存用戶信息的時候是這么做的,首先從服務器獲取用戶數(shù)據(jù),然后轉換成一個User對象使用GreenDao進行本地緩存,代碼如下:
/**
* 緩存用戶信息
*
* @param user
*/
public void cacheUserInfo(User user)
{
UserDao userDao = GreenDaoManager.getInstance().getmDaoSession().getUserDao();
userDao.save(user);
}
GreenDao里面提供存儲數(shù)據(jù)的方法有三個:
-
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是什么東東?這就是我今天被坑的地方,這個key就是我新增的Long id這個字段,也就是表中的主鍵 -
insert(T entity)
Insert an entity into the table associated with a concrete DAO.
插入實體類,這個是直接插入。 -
insertOrReplace(T entity)
Insert an entity into the table associated with a concrete DAO.將實體插入到與具體DAO關聯(lián)的表中,這是官方api的給的解釋,不過他的實際效果和svae很類似,就是如果存在就修改,不存在插入。
這里先說一下查詢的方法吧,官方提供查詢的方法有:
- loadByRowId(long rowId)
-
load(K key)
Loads the entity for the given PK.
根據(jù)主鍵獲取對象,也就是通過id獲取
但是我只有實體類的memberId,也就是在服務器上生成的Id,所以肯定沒法用這兩個方法獲取到數(shù)據(jù),然后又查文檔,發(fā)現(xiàn)[QueryBuilder<T>]http://greenrobot.org/files/greendao/javadoc/current/ ,在QueryBuilder中提供了幾個方法:
-
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對象構建一個Query<T>的對象,可以重復反復的利用這個去進行獲取數(shù)據(jù)
入坑ing
我是怎么入坑的? 當時我看了文檔之后發(fā)現(xiàn)save挺智能的嘛,就尋思著用save吧,自身就是在插入前做個判斷,省時省力,然后就有了上面的那段代碼,然后我在就在個人中心去獲取緩存的數(shù)據(jù),方法如下:
/**
* 從本地緩存中獲取user對象
*
* @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();
}
看起來沒有什么問題,然后我就去跑程序測試,第一次發(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等于當前用戶的,打印輸出果然數(shù)據(jù)庫有兩條數(shù)據(jù),然后找原因,肯定是在緩存的方法中去找,發(fā)現(xiàn)save也沒什么問題啊,想不通的時候必殺絕招就是看源碼
出坑
save的源碼如下:
public void save(T entity) {
if (hasKey(entity)) {
update(entity);
} else {
insert(entity);
}
}
大家可以看到其實他就是update和insert的一個判斷使用,有個hasKey的方法,然后查看hasKey的源碼:
abstract protected boolean hasKey(T entity);
發(fā)現(xiàn)是一個抽象方法,那我只能去他的繼承類UserDao里面去找了
@Override
public boolean hasKey(User entity)
{
return entity.getId() != null;
}
終于找到了真兇,沒錯 ,就是我們定義的Long id這個字段,原來他在插入之前是判斷了這個是否存在,如果不存在就執(zhí)行插入,如果存在就更新,然后就明白了,我在插入數(shù)據(jù)的時候是從服務器獲取到的數(shù)據(jù),只有memberId,而沒有本地數(shù)據(jù)庫的id,所以第二次緩存的時候仍然是執(zhí)行了insert的操作,然后在查詢的時候肯定出現(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);
}
在插入之前坐了個判斷,判斷是否有memberId相同的存在,如果存在取出GreenDao對應的id,放入user,這樣在hasKey判斷的時候肯定是執(zhí)行update了。ok,問題解決。不過,還有一種可能,說可不可能不添加Long id這個字段,直接給我們的memberId添加@Id呢,實際上試試不行的。大家看下面修改之后的代碼就知道了:
@Entity
public class User extends BaseBean
{
private int memberSex;//性別
private String memberLastX;//X幣
private String memberNickname;//昵稱
private String memberIcon;//頭像地址鏈接
private String memberMobile;//手機號
@Id
private int memberId;//用戶ID
private String memberDetailAddr;//用戶的詳細地址
private String memberLastExperience;//用戶經驗值
private String memberLevelName;//用戶等級昵稱
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");
}
變成了直接拋出異常了,所以,這個想法大家就別想了,如果有更好的辦法歡迎大家下方留言告知。謝謝。
我每天都會更新一個小知識給大家,如果有興趣可以關注,指不定什么時候就可以幫到你了,謝謝大家。