公司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条件。
不知道大伙还有别的更好的解决方案吗?