Sqlite之主键乱序和GreenDao设计的坑

公司Android项目使用的ORM框架是Greendao,曾经出现过一个bug:数据库中读出来的缓存数据顺序和存入的顺序不一样,不知大家是否也遇到?当时找到了几种解决方案,但是没有去探究具体的原因。

Sqlite rowId什么鬼?

如果我们使用可视化工具打开sqlite的数据库文件,你可以看到每个表中第一列都是rowid,这是谁定义的?

SQLITE会默认给所有的表增加一个rowid列,它会和Integer类型的主键绑定起来,官方文档中的说法:"INTEGER PRIMARY KEY" means that the column is an alias for the rowid.你能想象到比这这个扯淡的事情么?也就是说,你自己定义了一个Integer类型的主键,那么你给这个主键的所有赋值都会copyrowid,这样的话取出来的数据会按照rowid升序排的。

SQLITE数据库还有很多奇怪的其它特性,比如主键是可以为空,为空的主键还可以插入多个。。知道真相的我眼泪掉下来。。。

Greendao对主键的处理

既然Sqlite给我们留了这么美好的一个坑,GreenDao坚定地走在了踩坑的路上,我们一般定义一个主键:

 e.addIntProperty("id").primaryKey().notNull();
 
 or
 
 e.addLongProperty("id").primaryKey().notNull();

那么GreenDao如何处理的呢?Schema.java中的代码:

     //-----------------------------------------------
     propertyToDbType.put(PropertyType.Boolean, "INTEGER");
    propertyToDbType.put(PropertyType.Byte, "INTEGER");
    propertyToDbType.put(PropertyType.Short, "INTEGER");
    propertyToDbType.put(PropertyType.Int, "INTEGER");
    propertyToDbType.put(PropertyType.Long, "INTEGER");
    //------------知道真相的我眼泪再次留下来。。-------------
    propertyToDbType.put(PropertyType.Float, "REAL");
    propertyToDbType.put(PropertyType.Double, "REAL");
    propertyToDbType.put(PropertyType.String, "TEXT");
    propertyToDbType.put(PropertyType.ByteArray, "BLOB");
    propertyToDbType.put(PropertyType.Date, "INTEGER");

这么多类型被GreenDao处理成了INTEGER。这样我们在GreenDao里面定义的非小数类型的主键都会和rowid进行绑定,WTF!!

解决方案

提供之前想的几种折衷方案,但是感觉再怎么样都没有直接去修改GreenDao的源码来的直接。

  1. 将主键改成String类型,然后在代码里面做数值字符转化。这种写法是最省事的,因为我们这边项目是和Gson配合使用,Gson是支持 String<----->Number 自动转化的。如果表里面的数据量不大,那么是可以接受的,如果数据量较大,还是考虑第二种方案吧。
  2. 取消将业务的ID设置成主键,只设置成unique,让rowid行使主键的职责,这也是sqlite引入的目的。但是GreenDao上层的Dao.load(PK)支持对PK的直接查询,所以以后查表时需要封入相应的Where条件。

不知道大伙还有别的更好的解决方案吗?

你可能感兴趣的:(Sqlite之主键乱序和GreenDao设计的坑)