入坑前的铺垫
昨天在上一篇[GreenDao项目接入]http://www.jianshu.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;//用户所在地
}
上面为我实际开发中用来获取接口用户信息的实体类,然后我按照步骤对实体类进行了注释,如下:
@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中开发的),正是这个ID,让我掉了一次坑,给大家详细的描述下当时我遇到的问题以及解决办法:
我按照上面的实体类去Make project生成对应的UserDao和DaoMaster、DaoSession。然后我在代码里面缓存用户信息的时候是这么做的,首先从服务器获取用户数据,然后转换成一个User对象使用GreenDao进行本地缓存,代码如下:
/**
* 缓存用户信息
*
* @param user
*/
public void cacheUserInfo(User user)
{
UserDao userDao = GreenDaoManager.getInstance().getmDaoSession().getUserDao();
userDao.save(user);
}
GreenDao里面提供存储数据的方法有三个:
-
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属性判断是否存在,如果存在就更新数据,如果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关联的表中,这是官方api的给的解释,不过他的实际效果和svae很类似,就是如果存在就修改,不存在插入。
这里先说一下查询的方法吧,官方提供查询的方法有:
- loadByRowId(long rowId)
-
load(K key)
Loads the entity for the given PK.
根据主键获取对象,也就是通过id获取
但是我只有实体类的memberId,也就是在服务器上生成的Id,所以肯定没法用这两个方法获取到数据,然后又查文档,发现[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>的对象,可以重复反复的利用这个去进行获取数据
入坑ing
我是怎么入坑的? 当时我看了文档之后发现save挺智能的嘛,就寻思着用save吧,自身就是在插入前做个判断,省时省力,然后就有了上面的那段代码,然后我在就在个人中心去获取缓存的数据,方法如下:
/**
* 从本地缓存中获取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();
}
看起来没有什么问题,然后我就去跑程序测试,第一次发现吆喝,果然好使,执行完后数据库果然有了一行数据,屁颠的退出再来一遍,竟然崩了,一看日志提示:Expected unique result, but count was 2,意思就是说unique只能查询数据库中有一条符合条件的数据,上面的方法中我的查询条件是.where(UserDao.Properties.MemberId.eq(memberId)),也就是memberId等于当前用户的,打印输出果然数据库有两条数据,然后找原因,肯定是在缓存的方法中去找,发现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);
发现是一个抽象方法,那我只能去他的继承类UserDao里面去找了
@Override
public boolean hasKey(User entity)
{
return entity.getId() != null;
}
终于找到了真凶,没错 ,就是我们定义的Long id这个字段,原来他在插入之前是判断了这个是否存在,如果不存在就执行插入,如果存在就更新,然后就明白了,我在插入数据的时候是从服务器获取到的数据,只有memberId,而没有本地数据库的id,所以第二次缓存的时候仍然是执行了insert的操作,然后在查询的时候肯定出现了问题,真相大白就可以想办法解决了。下面是我的解决代码:
/**
* 缓存用户信息
*
* @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判断的时候肯定是执行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,执行了下出现问题了,然后去查看hasKey的代码:
@Override
public boolean hasKey(User entity) {
throw new UnsupportedOperationException("Unsupported for entities with a non-null key");
}
变成了直接抛出异常了,所以,这个想法大家就别想了,如果有更好的办法欢迎大家下方留言告知。谢谢。
我每天都会更新一个小知识给大家,如果有兴趣可以关注,指不定什么时候就可以帮到你了,谢谢大家。