GreenDao 增删改查使用中的一些坑

阅读 1828
收藏 35
2017-02-21
原文链接:www.jianshu.com

入坑前的铺垫

昨天在上一篇[GreenDao项目接入]www.jianshu.com/p/dac3bd9ba… 中说明了在项目中使用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>]greenrobot.org/files/green… ,在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");
    }

变成了直接抛出异常了,所以,这个想法大家就别想了,如果有更好的办法欢迎大家下方留言告知。谢谢。
我每天都会更新一个小知识给大家,如果有兴趣可以关注,指不定什么时候就可以帮到你了,谢谢大家。

评论