公司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类型的主键,那么你给这个主键的所有赋值都会copy给rowid,这样的话取出来的数据会按照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的源码来的直接。
- 将主键改成
String类型,然后在代码里面做数值字符转化。这种写法是最省事的,因为我们这边项目是和Gson配合使用,Gson是支持String<----->Number自动转化的。如果表里面的数据量不大,那么是可以接受的,如果数据量较大,还是考虑第二种方案吧。 - 取消将业务的
ID设置成主键,只设置成unique,让rowid行使主键的职责,这也是sqlite引入的目的。但是GreenDao上层的Dao.load(PK)支持对PK的直接查询,所以以后查表时需要封入相应的Where条件。
不知道大伙还有别的更好的解决方案吗?













网友评论