从查询中组装 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 中的错误。当运行我的初始查询时,没有什么是房间不能自己处理的。
我正在为我的应用程序开发一个数据库。我有以下 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 中的错误。当运行我的初始查询时,没有什么是房间不能自己处理的。