【问题标题】:Multiple fields have the same columnName Android Room多个字段具有相同的columnName Android Room
【发布时间】:2020-06-07 00:51:54
【问题描述】:

我有 3 个表 ruser、accounts、accountgroup。每一个都有一个名为 rsuerId 的相同列。

我创建了一个带有 3 个嵌入式对象的 POJO 类,如下所示。

class GroupChatItem(
    @Embedded
    val rUserDto: RUserDto,
    @Embedded
    val account: AccountDto,
    @Embedded
    val accountGroup: AccountGroupDto
)

现在,我想进行一个查询,以获取具有给定 rUserId 和 accountGroupId 的 GroupChatItem,如下所示。

@Query("""
        Select ruser.*, accounts.*, accountgroup.*
        from ruser 
        inner join accounts on accounts.rUserId = ruser.rUserId and accounts.active = 1 
        inner join accountgroup on accountgroup.rUserId = :rUserId and accountGroup.accountGroupId = :accountGroupId
        where ruser.rUserId = :rUserId
    """)
    suspend fun getGroupChatItem(rUserId: Long, accountGroupId: Int): GroupChatItem

不幸的是,我收到以下错误。

 Multiple fields have the same columnName: rUserId. Field names: rUserDto > rUserId, account > rUserId, accountGroup > rUserId.

我试图为每个嵌入对象添加一个前缀,但我也得到了一个错误。我不想一一检索列,因为它们有很多。 有什么我错过的......?? 谢谢

【问题讨论】:

  • 不行,你需要一一检索它们,在生产代码中使用select *通常被认为是不好的做法。
  • 错误信息的哪一部分您不清楚?您有多个具有相同名称的列。

标签: mysql android-room pojo


【解决方案1】:

您也可以使用嵌入式注释的prefix attribute

class GroupChatItem(
    @Embedded(prefix = "user_")
    val rUserDto: RUserDto,
    @Embedded(prefix = "acc_")
    val account: AccountDto,
    @Embedded(prefix = "accgr_")
    val accountGroup: AccountGroupDto
)

然后为 SQL 查询中每个实体的所有列设置别名。

我认为前缀属性是最近的更新,但我不确定

【讨论】:

    【解决方案2】:

    我不相信你除了拥有/使用之外别无选择:-

    • a) 在要包含在连接中的表中具有不同的列名(这样就不需要为列名添加前缀),

    • b) 在提取值时使用 AS 重命名列以及嵌入实体时的前缀,以确保名称匹配。

    我相信 a) 会是更简单的选择,因为可以减少无意中使用错误列名的机会。


    据我了解,列名必须匹配,Room 才能知道如何从底层结果集中复制值,这并没有表明值来自哪个表返回的一个或多个对象。

    这是一个类似场景 3 嵌入式实体(UserOfficePlaces)的生成代码示例,其中一些列名相同。它们每个都有 id 列,并且 User 和 Places 都有一个名为 name 的列。

    @Override
    public UserOfficePlacesCombined getAllUserOfficePlacesCombined() {
        final String _sql = "SELECT user.id AS userid, user.name AS username, office.id AS officeid, office.address AS officeaddress, places.id AS placesid, places.name AS placesname FROM User JOIN Office ON User.id = Office.id JOIN Places ON User.id = Places.id";
        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
        __db.assertNotSuspendingTransaction();
        final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
        try {
            final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "userid");
            final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "username");
            final int _cursorIndexOfId_1 = CursorUtil.getColumnIndexOrThrow(_cursor, "officeid");
            final int _cursorIndexOfAddress = CursorUtil.getColumnIndexOrThrow(_cursor, "officeaddress");
            final int _cursorIndexOfId_2 = CursorUtil.getColumnIndexOrThrow(_cursor, "placesid");
            final int _cursorIndexOfName_1 = CursorUtil.getColumnIndexOrThrow(_cursor, "placesname");
            final UserOfficePlacesCombined _result;
            if(_cursor.moveToFirst()) {
                final User _tmpUser;
                if (! (_cursor.isNull(_cursorIndexOfId) && _cursor.isNull(_cursorIndexOfName))) {
                    final long _tmpId;
                    _tmpId = _cursor.getLong(_cursorIndexOfId);
                    final String _tmpName;
                    _tmpName = _cursor.getString(_cursorIndexOfName);
                    _tmpUser = new User(_tmpId,_tmpName);
                }  else  {
                    _tmpUser = null;
                }
                final Office _tmpOffice;
                if (! (_cursor.isNull(_cursorIndexOfId_1) && _cursor.isNull(_cursorIndexOfAddress))) {
                    final long _tmpId_1;
                    _tmpId_1 = _cursor.getLong(_cursorIndexOfId_1);
                    final String _tmpAddress;
                    _tmpAddress = _cursor.getString(_cursorIndexOfAddress);
                    _tmpOffice = new Office(_tmpId_1,_tmpAddress);
                }  else  {
                    _tmpOffice = null;
                }
                final Places _tmpPlaces;
                if (! (_cursor.isNull(_cursorIndexOfId_2) && _cursor.isNull(_cursorIndexOfName_1))) {
                    final long _tmpId_2;
                    _tmpId_2 = _cursor.getLong(_cursorIndexOfId_2);
                    final String _tmpName_1;
                    _tmpName_1 = _cursor.getString(_cursorIndexOfName_1);
                    _tmpPlaces = new Places(_tmpId_2,_tmpName_1);
                }  else  {
                    _tmpPlaces = null;
                }
                _result = new UserOfficePlacesCombined();
                _result.setUser(_tmpUser);
                _result.setOffice(_tmpOffice);
                _result.setPlaces(_tmpPlaces);
            } else {
                _result = null;
            }
            return _result;
        } finally {
            _cursor.close();
            _statement.release();
        }
    }
    

    关键线是这样的:-

     final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "userid")
    

    这用于在游标(也称为结果集)中搜索列的名称并将偏移量返回到列,然后使用索引从游标中获取实际值。

    在您的场景中,结果集将包括一些类似

    • rUserId rUserId rUserId*

    它应该用于哪个?您可能知道/理解第一个是 ruser.rUserId,第二个是 account.rUserId,第三个是 accountgroup.rUserId,但 Room 就目前而言,不会知道何时生成代码。因此,在使用 getColumnIndex("rUserId") 的所有 3 个实例中,它将返回 0(第一个)它跳出循环,或者如果它继续而不是跳出循环,则返回 2(我相信它不会跳出循环)。

    【讨论】:

    • 试图在表中使用不同的列名的问题是,它们通常最终会通过添加试图避免的前缀而变得不同;然后数据库增长并且前缀与多个表相关,所以你必须记住哪个表使用了哪个前缀,最终不得不用你最初试图避免的表/别名来限定名称,所以查询结束看起来像....runbigdata AS rbg JOIN runbaddata AS rbd ON rbg.rbdvalue = rbd.rbdvalue....
    • @Uueerdo 我不会讨论是否支持糟糕的设计。如果您认为 Room 应该适应糟糕的设计,请与 Room 的负责人联系。
    • 那么有什么方法可以从查询中排除特定列...?
    • @james04 您可以通过专门包含其他列来排除列,但您可能会遇到无法生成基础对象的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    • 2022-09-30
    相关资源
    最近更新 更多