2

我目前正在尝试使用包含(字节数组)属性的 where 条件的查询来选择条目。此属性/列包含一个序列化的 UUID。不幸的是,我目前无法更改此列的数据类型,因为数据库是由单独的模块创建和同步的,该模块仅适用于当前实现。由于 greenDao 无法正确处理字节数组作为主键,我正试图以某种方式解决这个问题。创建我自己的选择查询等将是一个解决方案该属性在 greenDAO 生成期间定义为:

Entity randomEntity = schema.addEntity("RandomEntity");
...
randomEntity.addByteArrayProperty("RandomProperty"); 


查询是使用以下行构建的:

Query query = this.mRandomEntityDao.queryBuilder().where(RandomEntityDao.Properties.RandomProperty.eq(randomByteArray)).build();


不幸的是,此操作出现以下错误:

de.greenrobot.dao.DaoException: Illegal value: found array, but simple object required
at de.greenrobot.dao.query.WhereCondition$PropertyCondition.checkValueForType(WhereCondition.java:75)
...


这是greenDAO根本不支持条件还是我错过了一些重要的东西?不幸的是,我不能为这个特定属性使用其他数据类型。

编辑:

我当前的解决方法如下:
由于 greenDao 可以处理作为字符串的主键(直到某个点)并且 UUID 也可以用这种数据类型表示,所以我更改了现有表并添加了以下列:

db.execSQL("ALTER TABLE 'RANDOMTABLE' ADD COLUMN '_GREENID' TEXT;");


同步模块忽略此列,因此不应该有任何问题。然后我创建了一个触发器,将列中的序列化 UUID 映射ID到新_GREENID列:

db.execSQL("CREATE TRIGGER randomtableGreen AFTER INSERT ON 'RANDOMTABLE' BEGIN " + 
"UPDATE 'RANDOMTABLE' SET '_GREENID' = HEX(NEW.ID) WHERE 'RANDOMTABLE'.ID = NEW.ID; " +
"END;");


最后,我在表上运行更新,以防它们在创建触发器之前已经包含一些条目:

db.execSQL("UPDATE 'RANDOMTABLE' SET '_GREENID' = HEX(ID) WHERE '_GREENID' <> '';");
4

1 回答 1

3

关于 greendao 中的 byte[] aka BLOB:

目前不支持查看de.greenrobot.dao.query.WhereCondition.PropertyCondition.checkValueForType条件byte[],因为如果 value 是 type ,以下几行将始终抛出异常byte[]

if (value != null && value.getClass().isArray()) {
    throw new DaoException("Illegal value: found array, but simple object required");
}

解决方案 1 - 修改并贡献于 greendao:

您可以修改上面的行,以便仅在 value 的类型和 Property 的类型不适合时才引发异常。

if (value != null) {
    if (value.getClass().isArray() && !property.type.isArray()) {
        throw new DaoException("Illegal value: found array, but " +
                "simple object required");
    }
    if (!value.getClass().isArray() && property.type.isArray()) {
        throw new DaoException("Illegal value: found simple object, " +
                "but array required");
    }
}

也许这已经解决了问题,但是 greendao 中的其他部分可能会停止使用此编辑,否则会破坏查询。例如,将参数绑定到查询可能不适用于数组。

解决方案 2 - 使用queryRaw(String where, String... selectionArg)

这很简单,对 SQLite 有一些了解应该不成问题。

解决方案 3 - 使用查找表

假设原表:

ORIG
-------------------------------
UUID     BLOB
...

您可以修改ORIG和添加自动增量主键:

db.execSQL("ALTER TABLE 'ORIG' " +
        "ADD COLUMN 'REF_ID' INT PRIMARYKEY AUTOINCREMENT;");

同步服务应该已经关心ORIG.UUID并忽略新的ORIG.REF_ID-column 的唯一性。为了插入新的 UUID,同步服务可能会使用INSERTORIG.REF_ID. 为了更新现有UUID的同步服务,可能会使用UPDATE ... WHERE UUID=?并且不会创建新的ORIG.REF_ID-value,但会保留旧的值。

总结ORIG-table 在 columnREF_ID和 column之间有一个新的双射UUID

现在您可以创建另一个表:

ORIG_IDX
------------------------------
UUID      TEXT PRIMARYKEY
REF_ID    INT  UNIQUE

(如果您的数据小于 8 个字节,它也将适合 a ,INTTEXT我不知道是否有内置的转换/转换 from BLOBto INT。)

ORIG.IDX.UUID将是 的字符串表示形式ORIG.UUIDORIG_IDX.REF_ID是 的外键ORIG.REF_ID

ORIG_IDX由触发器填充和更新:

db.execSQL("CREATE TRIGGER T_ORIG_AI AFTER INSERT ON 'ORIG' BEGIN " + 
        "INSERT 'ORIG_IDX' SET 'REF_ID' = NEW.REF_ID, 'UUID' = NEW.UUID" +
        "END;");

UPDATE为和创建相应的触发器DELETE

您可以创建表ORIGORIG_IDX使用 greendao,然后使用以下命令查询请求的 uuid:

public Orig getOrig(String uuid) {
    OrigIdx origIdx = OrigIdxDao.queryBuilder().where(
            QrigIdxDao.Properties.UUID.eq(uuid)).unique();
    if (origIdx != null) {
        return origIdx.getOrig();
    }
    return null;
}

我认为尚不支持 String-primarykey,因此dao.load(uuid)不可用。

关于扩展表:

您可以使用string主键列并在实体的保留部分中提供转换方法。在进行插入之前,您必须计算主键列。

如果有其他工具插入数据(例如您的同步服务),您必须在插入发生之前使用触发器来计算您的主键。使用 SQLite 似乎不可能。因此,同步服务插入时主键约束将失败,因此此解决方案不适用于主键!

于 2013-10-08T12:53:02.670 回答