从查询中组装 pojo 时,Room 分配了错误的值

Room assigns wrong values when assembling pojos from a query

我正在为我的应用程序开发一个数据库。我有以下 pojos:

abstract class BaseEntity {
    abstract val id: Long
}

@Entity(
    tableName = EntryDao.TABLE_NAME,
    foreignKeys = [ForeignKey(
        entity = HabitInstance::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("parentHabitInstanceId"),
        onDelete = CASCADE
    )], indices = [Index("completedAt")]
)
@Parcelize
data class Entry(
    @ColumnInfo(index = true) val parentHabitInstanceId: Long,
    val completedAt: Instant,
    @PrimaryKey(autoGenerate = true)
    override val id: Long = 0
) : BaseEntity(), Parcelable

@Entity(
    tableName = HabitInstanceDao.TABLE_NAME,
    foreignKeys = [ForeignKey(
        entity = Ritual::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("parentRitualId"),
        onDelete = CASCADE
    ), ForeignKey(
        entity = Habit::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("parentHabitId"),
        onDelete = CASCADE
    )],
    indices = [Index("parentRitualId"), Index("parentHabitId")]
)
@Parcelize
data class HabitInstance(
    val parentRitualId: Long,
    val parentHabitId: Long,
    @PrimaryKey(autoGenerate = true)
    override val id: Long = 0
) : BaseEntity(), Parcelable

data class Ritual( 
@PrimaryKey(autoGenerate = true)
    override val id: Long = 0
) : Parcelable, BaseEntity()

我需要一个查询来根据两件事检索条目:它们所属的仪式的 ID 和完成时间。经过多次努力并试图避免自定义查询,我没有设法完成这个并编写了我自己的查询。 dao中的方法如下:

@Query("""
        SELECT * FROM $TABLE_NAME AS E
        LEFT JOIN ${HabitInstanceDao.TABLE_NAME} AS I ON E.parentHabitInstanceId = I.id
            WHERE (E.completedAt >= :after) AND (I.parentRitualId=(:ritualId) )""")
    abstract fun getAfterByRitual(ritualId: Long, after: Instant): Flow<List<Entry>>

使用SQL语法格式:

SELECT * FROM Entry AS E
        LEFT JOIN HabitInstance AS I ON E.parentHabitInstanceId = I.id
            WHERE (E.completedAt >= :after) AND (I.parentRitualId=(:ritualId) )

我正在使用通用的 dao 实现,我的所有实体都具有从 BaseEntity 派生的 id 字段。这就是问题所在。 运行 由于某种原因,我在上面指定的查询使 Room 在分配 id 字段时混淆了它们。这意味着,查询运行良好并且 returns 值符合预期,但是它返回的所有条目都没有它们自己的 id,而是它们的习惯实例的 id属于,基本上,parentHabitInstanceId 将等于 id。我已通过 运行 仪器测试和调试应用程序确认了此行为。我本来可以将 id 字段的名称更改为唯一的,但是,我的通用 dao 实现不允许这样做。我认为这是 Room 中的错误,并尝试使用版本 2.4.0-alpha03 和 2.3.0 无济于事。房间没有发出警告也没有错误,但是,几个条目具有相同的 id,这应该是不可能的。我该如何解决这个问题? 这是 Room 生成的 EntryDao_Impl.java 的代码:

final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
          try {
            final int _cursorIndexOfParentHabitInstanceId = CursorUtil.getColumnIndexOrThrow(_cursor, "parentHabitInstanceId");
            final int _cursorIndexOfCompletedAt = CursorUtil.getColumnIndexOrThrow(_cursor, "completedAt");
            final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
            final int _cursorIndexOfId_1 = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
            final List<Entry> _result = new ArrayList<Entry>(_cursor.getCount());
            while(_cursor.moveToNext()) {
              final Entry _item;
              final long _tmpParentHabitInstanceId;
              _tmpParentHabitInstanceId = _cursor.getLong(_cursorIndexOfParentHabitInstanceId);
              final Instant _tmpCompletedAt;
              final Long _tmp_1;
              if (_cursor.isNull(_cursorIndexOfCompletedAt)) {
                _tmp_1 = null;
              } else {
                _tmp_1 = _cursor.getLong(_cursorIndexOfCompletedAt);
              }
              _tmpCompletedAt = __dBConverters.toInstant(_tmp_1);
              final long _tmpId;
              _tmpId = _cursor.getLong(_cursorIndexOfId);
              final long _tmpId_1;
              _tmpId_1 = _cursor.getLong(_cursorIndexOfId_1);
              _item = new Entry(_tmpParentHabitInstanceId,_tmpPointsDelta,_tmpJournal,_tmpCompletedAt,_tmpId);
              _result.add(_item);
            }
            __db.setTransactionSuccessful();
            return _result;

我们清楚地看到它只是默默地将 _1 附加到 id,但最终混淆了这些 id。如何在不重命名实体声明中的列的情况下帮助房间区分 ID?

我找到了修复查询的方法。简单的解决方法是明确指定需要包含在 POJO 中的列。所以,我的查询变成了:

SELECT E.parentHabitInstanceId, E.completedAt, E.id FROM $TABLE_NAME AS E
        LEFT JOIN ${HabitInstanceDao.TABLE_NAME} AS I ON E.parentHabitInstanceId = I.id
            WHERE (E.completedAt >= :after) AND ( I.parentRitualId=(:ritualId) )

现在这段代码通过了所有测试。 在生产代码中使用 SELECT * 是一种不好的做法,我已经忘记了它,因为 * 被广泛用于 Room。但是,我仍然认为这是 Room 中的错误。当运行我的初始查询时,没有什么是房间不能自己处理的。