为什么 Flutter Floor DAO findAll returns 实体设置为空 ID?

Why Flutter Floor DAO findAll returns entity set with null ids?

这是一个使用 Floor(相当于 Jetpack 的 Room 的包)的 Flutter 项目。 我有一个带有自动递增 ID 的实体(请注意,这是预空安全代码,在我现在从 post 空安全代码段开始的答案中):

const String ACTIVITIES_TABLE_NAME = 'activities';

@Entity(
  tableName: ACTIVITIES_TABLE_NAME,
  indices: [
    Index(value: ['start'])
  ],
)
class Activity {
  @PrimaryKey(autoGenerate: true)
  int id;
  @ColumnInfo(name: 'device_name')
  final String deviceName;
  @ColumnInfo(name: 'device_id')
  final String deviceId;
  final int start;

  Activity({
    this.deviceName,
    this.deviceId,
    this.start,
  });
}

我有这个 DAO:

@dao
abstract class ActivityDao {
  @Query('SELECT * FROM $ACTIVITIES_TABLE_NAME ORDER BY start DESC')
  Future<List<Activity>> findAllActivities();

  @insert
  Future<int> insertActivity(Activity activity);
}

到目前为止,我在我的应用程序中记录了五项活动,hunky-dory。

_database =
    await $FloorAppDatabase.databaseBuilder('app_database.db').build();
...
    _activity =
       Activity(deviceName: device.name, deviceId: device.id.id, start: _lastRecord.millisecondsSinceEpoch);
    final id = await _database.activityDao.insertActivity(_activity);
    _activity.id = id;

然后在另一个视图中列出它们,但是有一个潜伏的问题。

final data = await _database.activityDao.findAllActivities();

当我使用这种非常简单的方法查询实体时,我的数据库中所有大约五个活动都返回了一个 id 字段,其中填充了 null。这使得该实体完全无用,因为我想对其执行的任何其他操作都因缺少实际 ID 而失败。我做错了什么吗?

我主要使用 MySQL、Postgres 和非 SQLite RDBMS。据我了解,在 SQLite 中,每一行都有一个唯一的 rowid,并且通过声明我的自动增量 id 主键字段,基本上我为该 rowid 取了别名?不管是什么,我都需要身份证。不能为空。


我正在调试 Floor 的内核。

  Future<List<T>> queryList<T>(
    final String sql, {
    final List<dynamic> arguments,
    @required final T Function(Map<String, dynamic>) mapper,
  }) async {
    final rows = await _database.rawQuery(sql, arguments);
    return rows.map((row) => mapper(row)).toList();
  }

此时 rows 仍然正确填写了 ID,尽管行中的实体只是动态值列表。 rows.map 应该将它们映射到实体对象,并且由于某种原因不能携带 id?这可能是地板错误吗?


好的,现在我看到在生成的 database.g.dart 映射器中没有 id:

  static final _activitiesMapper = (Map<String, dynamic> row) => Activity(
      deviceName: row['device_name'] as String,
      deviceId: row['device_id'] as String,
      start: row['start'] as int);

这就解释了为什么 id 是 null。但这是生成的代码,我如何告诉 Floor 我需要 id?或者我为什么要告诉它,它必须默认存在,谁不想知道一个对象的主键?

好的,这是因为我没有将 id 作为构造函数参数。我没有那个,因为它是一个自动递增字段,我不是决定它的人。但是,如果没有将它作为构造函数参数,生成的代码就无法将它作为参数与映射器一起传递,因此它会将它从映射器中移除。所以我添加了 id 作为构造函数参数。

Post null-safety 版本:

class Activity {
  @PrimaryKey(autoGenerate: true)
  int? id;
  @ColumnInfo(name: 'device_name')
  final String deviceName;
  @ColumnInfo(name: 'device_id')
  final String deviceId;
  final int start;
  final int end;

  Activity({
    this.id,
    required this.deviceName,
    required this.deviceId,
    required this.start,
    this.end: 0,
  });
}

这个Floor GitHub问题我也加在这里,看来以后可能会有注解:https://github.com/vitusortner/floor/issues/527


前 null-safety 版本:

import 'package:meta/meta.dart';

class Activity {
  @PrimaryKey(autoGenerate: true)
  int id;
  @ColumnInfo(name: 'device_name')
  @required
  final String deviceName;
  @ColumnInfo(name: 'device_id')
  @required
  final String deviceId;
  final int start;

  Activity({
    this.id: null,
    this.deviceName,
    this.deviceId,
    this.start,
  });
}

编译器对 null 有点不确定,但在 flutter packages pub run build_runner build 之后 database.g.dart 的映射器也有 id